retransmit.dev 0.1.2 → 0.3.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 CHANGED
@@ -1,12 +1,12 @@
1
1
  # retransmit.dev
2
2
 
3
- Node.js SDK for the [Retransmit](https://retransmit.dev) messaging API. Send email and SMS through one typed client; WhatsApp support is coming soon. Zero dependencies, works on Node 18+ and edge runtimes with `fetch`.
3
+ Node.js SDK for the [Retransmit](https://retransmit.dev) messaging API. Send email, SMS, and WhatsApp through one typed client. Zero dependencies, works on Node 18+ and edge runtimes with `fetch`.
4
4
 
5
5
  | Channel | SDK namespace | Availability |
6
6
  | --- | --- | --- |
7
7
  | Email | `retransmit.emails` | Available |
8
8
  | SMS | `retransmit.sms` | Available |
9
- | WhatsApp | `retransmit.whatsapp` | Coming soon; not included in the current release |
9
+ | WhatsApp | `retransmit.whatsapp` | Available |
10
10
 
11
11
  ## Install
12
12
 
@@ -51,6 +51,57 @@ const { data } = await retransmit.emails.get("em_xxxxxxxxxxxx");
51
51
  console.log(data?.status); // "delivered"
52
52
  ```
53
53
 
54
+ ### Tags
55
+
56
+ Attach up to 10 `{ name, value }` tags to an email to label it. Filter by
57
+ them in the dashboard or with `emails.list`. Tags come back on `emails.get`
58
+ and are never sent to the recipient.
59
+
60
+ ```ts
61
+ await retransmit.emails.send({
62
+ from: "Acme <hello@yourdomain.com>",
63
+ to: "user@example.com",
64
+ subject: "Your receipt",
65
+ html: "<p>Thanks for your order!</p>",
66
+ tags: [
67
+ { name: "category", value: "receipt" },
68
+ { name: "campaign", value: "spring-2026" },
69
+ ],
70
+ });
71
+ ```
72
+
73
+ Names and values allow letters, digits, underscores and dashes, up to 256
74
+ characters each. Names must be unique within one email. Batch emails accept
75
+ the same `tags` field.
76
+
77
+ ### List and filter emails
78
+
79
+ `emails.list` returns your emails newest first. Every tag you pass must match.
80
+ Combine with `status` or `batchId`, and page with `cursor`:
81
+
82
+ ```ts
83
+ const { data } = await retransmit.emails.list({
84
+ tags: [{ name: "campaign", value: "spring-2026" }],
85
+ status: "bounced",
86
+ limit: 100,
87
+ });
88
+
89
+ for (const email of data!.emails) {
90
+ console.log(email.id, email.to, email.status, email.tags);
91
+ }
92
+
93
+ if (data!.has_more) {
94
+ await retransmit.emails.list({ cursor: data!.next_cursor!, /* same filters */ });
95
+ }
96
+ ```
97
+
98
+ To see which tags exist on your account, and how many emails carry each:
99
+
100
+ ```ts
101
+ const { data } = await retransmit.emails.tags();
102
+ // data.tags: [{ name: "campaign", value: "spring-2026", count: 1240 }, ...]
103
+ ```
104
+
54
105
  ## SMS
55
106
 
56
107
  Use international E.164 phone numbers. A single request can contain up to 50
@@ -93,10 +144,33 @@ console.log(progress?.processed, "/", progress?.total, progress?.counts);
93
144
 
94
145
  ## WhatsApp
95
146
 
96
- WhatsApp is the next planned channel. The intended SDK namespace is
97
- `retransmit.whatsapp`, but it is deliberately not exported yet because the
98
- production endpoint and request types are still being finalized. This keeps
99
- the current SDK honest while reserving a consistent channel-based API shape.
147
+ Connect your WhatsApp Business number in the dashboard first. One recipient
148
+ per request, in E.164 format. Start a conversation with an approved template;
149
+ once the recipient replies you can send free-form text and media for 24 hours.
150
+
151
+ ```ts
152
+ const { data, error } = await retransmit.whatsapp.send({
153
+ to: "+237670000000",
154
+ type: "template",
155
+ template: {
156
+ name: "verification_code",
157
+ language: "en_US",
158
+ components: [{ type: "body", parameters: [{ type: "text", text: "482913" }] }],
159
+ },
160
+ });
161
+
162
+ // Inside the 24-hour window:
163
+ await retransmit.whatsapp.send({ to: "+237670000000", text: "Anything else we can help with?" });
164
+ ```
165
+
166
+ Check the outcome, including `read` receipts:
167
+
168
+ ```ts
169
+ const { data } = await retransmit.whatsapp.get("wa_xxxxxxxxxxxx");
170
+ console.log(data?.status); // "sent" | "delivered" | "read" | "failed"
171
+ ```
172
+
173
+ Replies from recipients reach you as `whatsapp.received` webhooks.
100
174
 
101
175
  ## Error handling
102
176
 
package/dist/index.cjs CHANGED
@@ -25,7 +25,9 @@ __export(index_exports, {
25
25
  Emails: () => Emails,
26
26
  Retransmit: () => Retransmit,
27
27
  SMS_STATUSES: () => SMS_STATUSES,
28
- Sms: () => Sms
28
+ Sms: () => Sms,
29
+ WHATSAPP_STATUSES: () => WHATSAPP_STATUSES,
30
+ Whatsapp: () => Whatsapp
29
31
  });
30
32
  module.exports = __toCommonJS(index_exports);
31
33
 
@@ -40,7 +42,8 @@ function toWirePayload(options) {
40
42
  subject: options.subject,
41
43
  html: options.html,
42
44
  text: options.text,
43
- marketing: options.marketing
45
+ marketing: options.marketing,
46
+ tags: options.tags
44
47
  };
45
48
  }
46
49
  var Emails = class {
@@ -56,6 +59,23 @@ var Emails = class {
56
59
  get(id) {
57
60
  return this.client.request("GET", `/v1/emails/${encodeURIComponent(id)}`);
58
61
  }
62
+ /**
63
+ * Lists your emails, newest first, optionally filtered by tags, status or
64
+ * batch. Pass `next_cursor` back as `cursor` to page through the results.
65
+ */
66
+ list(options = {}) {
67
+ return this.client.request("GET", "/v1/emails", void 0, {
68
+ tag: options.tags?.map((tag) => `${tag.name}:${tag.value}`),
69
+ status: options.status,
70
+ batch_id: options.batchId,
71
+ limit: options.limit,
72
+ cursor: options.cursor
73
+ });
74
+ }
75
+ /** Every distinct tag on your emails, with a count of emails carrying it. */
76
+ tags() {
77
+ return this.client.request("GET", "/v1/emails/tags");
78
+ }
59
79
  };
60
80
 
61
81
  // src/batch.ts
@@ -96,15 +116,45 @@ var Sms = class {
96
116
  }
97
117
  };
98
118
 
119
+ // src/whatsapp.ts
120
+ var Whatsapp = class {
121
+ constructor(client) {
122
+ this.client = client;
123
+ }
124
+ client;
125
+ /**
126
+ * Queues a single WhatsApp message to one recipient. Poll `get(id)` or
127
+ * subscribe to webhooks for the outcome; replies arrive as
128
+ * `whatsapp.received` webhooks.
129
+ */
130
+ send(options) {
131
+ return this.client.request("POST", "/v1/whatsapp", {
132
+ from: options.from,
133
+ to: options.to,
134
+ type: options.type,
135
+ text: options.text,
136
+ preview_url: options.previewUrl,
137
+ template: options.template,
138
+ image: options.image,
139
+ document: options.document
140
+ });
141
+ }
142
+ /** Retrieves a WhatsApp message with its current status and event history. */
143
+ get(id) {
144
+ return this.client.request("GET", `/v1/whatsapp/${encodeURIComponent(id)}`);
145
+ }
146
+ };
147
+
99
148
  // src/retransmit.ts
100
149
  var DEFAULT_BASE_URL = "https://api.retransmit.dev";
101
- var USER_AGENT = "retransmit.dev-node/0.1.0";
150
+ var USER_AGENT = "retransmit.dev-node/0.3.0";
102
151
  function readEnv(name) {
103
152
  return typeof process !== "undefined" ? process.env?.[name] : void 0;
104
153
  }
105
154
  var Retransmit = class {
106
155
  emails = new Emails(this);
107
156
  sms = new Sms(this);
157
+ whatsapp = new Whatsapp(this);
108
158
  batch = new Batch(this);
109
159
  apiKey;
110
160
  baseUrl;
@@ -122,10 +172,17 @@ var Retransmit = class {
122
172
  );
123
173
  }
124
174
  /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
125
- async request(method, path, body) {
175
+ async request(method, path, body, query) {
176
+ const params = new URLSearchParams();
177
+ for (const [key, value] of Object.entries(query ?? {})) {
178
+ if (value === void 0) continue;
179
+ for (const item of Array.isArray(value) ? value : [value]) params.append(key, String(item));
180
+ }
181
+ const encoded = params.toString();
182
+ const search = encoded ? `?${encoded}` : "";
126
183
  let response;
127
184
  try {
128
- response = await fetch(`${this.baseUrl}${path}`, {
185
+ response = await fetch(`${this.baseUrl}${path}${search}`, {
129
186
  method,
130
187
  headers: {
131
188
  Authorization: `Bearer ${this.apiKey}`,
@@ -187,6 +244,7 @@ var SMS_STATUSES = [
187
244
  "rejected",
188
245
  "failed"
189
246
  ];
247
+ var WHATSAPP_STATUSES = ["queued", "sent", "delivered", "read", "failed"];
190
248
  // Annotate the CommonJS export names for ESM import in node:
191
249
  0 && (module.exports = {
192
250
  Batch,
@@ -194,5 +252,7 @@ var SMS_STATUSES = [
194
252
  Emails,
195
253
  Retransmit,
196
254
  SMS_STATUSES,
197
- Sms
255
+ Sms,
256
+ WHATSAPP_STATUSES,
257
+ Whatsapp
198
258
  });
package/dist/index.d.cts CHANGED
@@ -39,6 +39,18 @@ interface SendEmailOptions {
39
39
  * headers, and skips recipients who already unsubscribed.
40
40
  */
41
41
  marketing?: boolean;
42
+ /**
43
+ * Up to 10 labels for filtering emails in the dashboard, for example
44
+ * `[{ name: "campaign", value: "outreach-1" }]`. Names and values allow
45
+ * letters, digits, underscores and dashes, up to 256 characters each.
46
+ * Names must be unique within one email. Tags are returned by `emails.get`
47
+ * and are never sent to the recipient.
48
+ */
49
+ tags?: EmailTag[];
50
+ }
51
+ interface EmailTag {
52
+ name: string;
53
+ value: string;
42
54
  }
43
55
  interface SendEmailResponse {
44
56
  id: string;
@@ -59,12 +71,55 @@ interface GetEmailResponse {
59
71
  reply_to: string[] | null;
60
72
  subject: string;
61
73
  marketing: boolean;
74
+ tags: EmailTag[];
62
75
  status: EmailStatus;
63
76
  error: string | null;
64
77
  created_at: string;
65
78
  last_event_at: string | null;
66
79
  events: EmailEvent[];
67
80
  }
81
+ interface ListEmailsOptions {
82
+ /**
83
+ * Only emails carrying every one of these tags, for example
84
+ * `[{ name: "campaign", value: "outreach-1" }]`.
85
+ */
86
+ tags?: EmailTag[];
87
+ status?: EmailStatus;
88
+ /** Only emails sent as part of this batch. */
89
+ batchId?: string;
90
+ /** Page size, 1 to 100. Defaults to 50. */
91
+ limit?: number;
92
+ /** `next_cursor` from the previous page. */
93
+ cursor?: string;
94
+ }
95
+ /** One row of `emails.list`. Call `emails.get(id)` for the event history. */
96
+ interface EmailSummary {
97
+ id: string;
98
+ batch_id: string | null;
99
+ from: string;
100
+ to: string[];
101
+ subject: string;
102
+ marketing: boolean;
103
+ tags: EmailTag[];
104
+ status: EmailStatus;
105
+ error: string | null;
106
+ created_at: string;
107
+ last_event_at: string | null;
108
+ }
109
+ interface ListEmailsResponse {
110
+ /** Newest first. */
111
+ emails: EmailSummary[];
112
+ has_more: boolean;
113
+ /** Pass back as `cursor` to fetch the next page. `null` on the last page. */
114
+ next_cursor: string | null;
115
+ }
116
+ interface EmailTagCount extends EmailTag {
117
+ /** How many of your emails carry this tag. */
118
+ count: number;
119
+ }
120
+ interface ListEmailTagsResponse {
121
+ tags: EmailTagCount[];
122
+ }
68
123
  declare const SMS_STATUSES: readonly ["queued", "sent", "delivered", "undelivered", "expired", "rejected", "failed"];
69
124
  type SmsStatus = (typeof SMS_STATUSES)[number];
70
125
  interface SendSmsOptions {
@@ -106,6 +161,81 @@ interface GetSmsResponse {
106
161
  last_event_at: string | null;
107
162
  events: SmsEvent[];
108
163
  }
164
+ declare const WHATSAPP_STATUSES: readonly ["queued", "sent", "delivered", "read", "failed"];
165
+ type WhatsappStatus = (typeof WHATSAPP_STATUSES)[number];
166
+ type WhatsappMessageType = "text" | "template" | "image" | "document";
167
+ interface WhatsappTemplate {
168
+ /** Template name as approved in WhatsApp Manager. */
169
+ name: string;
170
+ /** Language code the template was approved for, e.g. `en_US`. */
171
+ language: string;
172
+ /** Meta `components` (header/body/button parameters), passed through verbatim. */
173
+ components?: Record<string, unknown>[];
174
+ }
175
+ interface WhatsappMedia {
176
+ /** Public HTTPS link fetched at send time. */
177
+ link: string;
178
+ caption?: string;
179
+ }
180
+ interface WhatsappDocument extends WhatsappMedia {
181
+ /** File name shown to the recipient. */
182
+ filename?: string;
183
+ }
184
+ interface SendWhatsappOptions {
185
+ /**
186
+ * Connected number to send from, in international format. Optional when
187
+ * your organization has a single WhatsApp number.
188
+ */
189
+ from?: string;
190
+ /** One recipient in international format (`+237670000000`). */
191
+ to: string;
192
+ /** Defaults to `text`. */
193
+ type?: WhatsappMessageType;
194
+ /** Body for `text` messages (up to 4,096 characters), or a media caption. */
195
+ text?: string;
196
+ /** Render a preview for the first link in a `text` message. */
197
+ previewUrl?: boolean;
198
+ /** Required for `type: "template"`. Needed to start a conversation outside the 24h window. */
199
+ template?: WhatsappTemplate;
200
+ /** Required for `type: "image"`. */
201
+ image?: WhatsappMedia;
202
+ /** Required for `type: "document"`. */
203
+ document?: WhatsappDocument;
204
+ }
205
+ interface SendWhatsappResponse {
206
+ id: string;
207
+ status: "queued";
208
+ type: WhatsappMessageType;
209
+ /** The connected number the message goes out from. */
210
+ from: string;
211
+ /** ISO 3166-1 alpha-2 destination country detected from the number prefix. */
212
+ country: string | null;
213
+ created_at: string;
214
+ }
215
+ interface WhatsappEvent {
216
+ type: string;
217
+ created_at: string;
218
+ }
219
+ interface GetWhatsappResponse {
220
+ id: string;
221
+ from: string;
222
+ to: string;
223
+ country: string | null;
224
+ type: WhatsappMessageType;
225
+ text: string | null;
226
+ preview_url: boolean;
227
+ template: WhatsappTemplate | null;
228
+ media: WhatsappDocument | null;
229
+ /** Upstream provider that carried the message, e.g. `meta`. */
230
+ provider: string | null;
231
+ /** Provider-side message id (`wamid.…` on Meta); inbound replies reference it. */
232
+ provider_message_id: string | null;
233
+ status: WhatsappStatus;
234
+ error: string | null;
235
+ created_at: string;
236
+ last_event_at: string | null;
237
+ events: WhatsappEvent[];
238
+ }
109
239
  interface SendBatchResponse {
110
240
  id: string;
111
241
  total: number;
@@ -138,6 +268,13 @@ declare class Emails {
138
268
  send(options: SendEmailOptions): Promise<Result<SendEmailResponse>>;
139
269
  /** Retrieves an email with its current status and event history. */
140
270
  get(id: string): Promise<Result<GetEmailResponse>>;
271
+ /**
272
+ * Lists your emails, newest first, optionally filtered by tags, status or
273
+ * batch. Pass `next_cursor` back as `cursor` to page through the results.
274
+ */
275
+ list(options?: ListEmailsOptions): Promise<Result<ListEmailsResponse>>;
276
+ /** Every distinct tag on your emails, with a count of emails carrying it. */
277
+ tags(): Promise<Result<ListEmailTagsResponse>>;
141
278
  }
142
279
 
143
280
  declare class Sms {
@@ -149,15 +286,29 @@ declare class Sms {
149
286
  get(id: string): Promise<Result<GetSmsResponse>>;
150
287
  }
151
288
 
289
+ declare class Whatsapp {
290
+ private readonly client;
291
+ constructor(client: Retransmit);
292
+ /**
293
+ * Queues a single WhatsApp message to one recipient. Poll `get(id)` or
294
+ * subscribe to webhooks for the outcome; replies arrive as
295
+ * `whatsapp.received` webhooks.
296
+ */
297
+ send(options: SendWhatsappOptions): Promise<Result<SendWhatsappResponse>>;
298
+ /** Retrieves a WhatsApp message with its current status and event history. */
299
+ get(id: string): Promise<Result<GetWhatsappResponse>>;
300
+ }
301
+
152
302
  declare class Retransmit {
153
303
  readonly emails: Emails;
154
304
  readonly sms: Sms;
305
+ readonly whatsapp: Whatsapp;
155
306
  readonly batch: Batch;
156
307
  private readonly apiKey;
157
308
  private readonly baseUrl;
158
309
  constructor(apiKey?: string, options?: RetransmitOptions);
159
310
  /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
160
- request<T>(method: "GET" | "POST", path: string, body?: unknown): Promise<Result<T>>;
311
+ request<T>(method: "GET" | "POST", path: string, body?: unknown, query?: Record<string, string | number | string[] | undefined>): Promise<Result<T>>;
161
312
  }
162
313
 
163
- export { Batch, EMAIL_STATUSES, type EmailEvent, type EmailStatus, Emails, type GetBatchResponse, type GetEmailResponse, type GetSmsResponse, type Result, Retransmit, type RetransmitError, type RetransmitOptions, SMS_STATUSES, type SendBatchResponse, type SendEmailOptions, type SendEmailResponse, type SendSmsOptions, type SendSmsResponse, Sms, type SmsEvent, type SmsStatus };
314
+ 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 };
package/dist/index.d.ts CHANGED
@@ -39,6 +39,18 @@ interface SendEmailOptions {
39
39
  * headers, and skips recipients who already unsubscribed.
40
40
  */
41
41
  marketing?: boolean;
42
+ /**
43
+ * Up to 10 labels for filtering emails in the dashboard, for example
44
+ * `[{ name: "campaign", value: "outreach-1" }]`. Names and values allow
45
+ * letters, digits, underscores and dashes, up to 256 characters each.
46
+ * Names must be unique within one email. Tags are returned by `emails.get`
47
+ * and are never sent to the recipient.
48
+ */
49
+ tags?: EmailTag[];
50
+ }
51
+ interface EmailTag {
52
+ name: string;
53
+ value: string;
42
54
  }
43
55
  interface SendEmailResponse {
44
56
  id: string;
@@ -59,12 +71,55 @@ interface GetEmailResponse {
59
71
  reply_to: string[] | null;
60
72
  subject: string;
61
73
  marketing: boolean;
74
+ tags: EmailTag[];
62
75
  status: EmailStatus;
63
76
  error: string | null;
64
77
  created_at: string;
65
78
  last_event_at: string | null;
66
79
  events: EmailEvent[];
67
80
  }
81
+ interface ListEmailsOptions {
82
+ /**
83
+ * Only emails carrying every one of these tags, for example
84
+ * `[{ name: "campaign", value: "outreach-1" }]`.
85
+ */
86
+ tags?: EmailTag[];
87
+ status?: EmailStatus;
88
+ /** Only emails sent as part of this batch. */
89
+ batchId?: string;
90
+ /** Page size, 1 to 100. Defaults to 50. */
91
+ limit?: number;
92
+ /** `next_cursor` from the previous page. */
93
+ cursor?: string;
94
+ }
95
+ /** One row of `emails.list`. Call `emails.get(id)` for the event history. */
96
+ interface EmailSummary {
97
+ id: string;
98
+ batch_id: string | null;
99
+ from: string;
100
+ to: string[];
101
+ subject: string;
102
+ marketing: boolean;
103
+ tags: EmailTag[];
104
+ status: EmailStatus;
105
+ error: string | null;
106
+ created_at: string;
107
+ last_event_at: string | null;
108
+ }
109
+ interface ListEmailsResponse {
110
+ /** Newest first. */
111
+ emails: EmailSummary[];
112
+ has_more: boolean;
113
+ /** Pass back as `cursor` to fetch the next page. `null` on the last page. */
114
+ next_cursor: string | null;
115
+ }
116
+ interface EmailTagCount extends EmailTag {
117
+ /** How many of your emails carry this tag. */
118
+ count: number;
119
+ }
120
+ interface ListEmailTagsResponse {
121
+ tags: EmailTagCount[];
122
+ }
68
123
  declare const SMS_STATUSES: readonly ["queued", "sent", "delivered", "undelivered", "expired", "rejected", "failed"];
69
124
  type SmsStatus = (typeof SMS_STATUSES)[number];
70
125
  interface SendSmsOptions {
@@ -106,6 +161,81 @@ interface GetSmsResponse {
106
161
  last_event_at: string | null;
107
162
  events: SmsEvent[];
108
163
  }
164
+ declare const WHATSAPP_STATUSES: readonly ["queued", "sent", "delivered", "read", "failed"];
165
+ type WhatsappStatus = (typeof WHATSAPP_STATUSES)[number];
166
+ type WhatsappMessageType = "text" | "template" | "image" | "document";
167
+ interface WhatsappTemplate {
168
+ /** Template name as approved in WhatsApp Manager. */
169
+ name: string;
170
+ /** Language code the template was approved for, e.g. `en_US`. */
171
+ language: string;
172
+ /** Meta `components` (header/body/button parameters), passed through verbatim. */
173
+ components?: Record<string, unknown>[];
174
+ }
175
+ interface WhatsappMedia {
176
+ /** Public HTTPS link fetched at send time. */
177
+ link: string;
178
+ caption?: string;
179
+ }
180
+ interface WhatsappDocument extends WhatsappMedia {
181
+ /** File name shown to the recipient. */
182
+ filename?: string;
183
+ }
184
+ interface SendWhatsappOptions {
185
+ /**
186
+ * Connected number to send from, in international format. Optional when
187
+ * your organization has a single WhatsApp number.
188
+ */
189
+ from?: string;
190
+ /** One recipient in international format (`+237670000000`). */
191
+ to: string;
192
+ /** Defaults to `text`. */
193
+ type?: WhatsappMessageType;
194
+ /** Body for `text` messages (up to 4,096 characters), or a media caption. */
195
+ text?: string;
196
+ /** Render a preview for the first link in a `text` message. */
197
+ previewUrl?: boolean;
198
+ /** Required for `type: "template"`. Needed to start a conversation outside the 24h window. */
199
+ template?: WhatsappTemplate;
200
+ /** Required for `type: "image"`. */
201
+ image?: WhatsappMedia;
202
+ /** Required for `type: "document"`. */
203
+ document?: WhatsappDocument;
204
+ }
205
+ interface SendWhatsappResponse {
206
+ id: string;
207
+ status: "queued";
208
+ type: WhatsappMessageType;
209
+ /** The connected number the message goes out from. */
210
+ from: string;
211
+ /** ISO 3166-1 alpha-2 destination country detected from the number prefix. */
212
+ country: string | null;
213
+ created_at: string;
214
+ }
215
+ interface WhatsappEvent {
216
+ type: string;
217
+ created_at: string;
218
+ }
219
+ interface GetWhatsappResponse {
220
+ id: string;
221
+ from: string;
222
+ to: string;
223
+ country: string | null;
224
+ type: WhatsappMessageType;
225
+ text: string | null;
226
+ preview_url: boolean;
227
+ template: WhatsappTemplate | null;
228
+ media: WhatsappDocument | null;
229
+ /** Upstream provider that carried the message, e.g. `meta`. */
230
+ provider: string | null;
231
+ /** Provider-side message id (`wamid.…` on Meta); inbound replies reference it. */
232
+ provider_message_id: string | null;
233
+ status: WhatsappStatus;
234
+ error: string | null;
235
+ created_at: string;
236
+ last_event_at: string | null;
237
+ events: WhatsappEvent[];
238
+ }
109
239
  interface SendBatchResponse {
110
240
  id: string;
111
241
  total: number;
@@ -138,6 +268,13 @@ declare class Emails {
138
268
  send(options: SendEmailOptions): Promise<Result<SendEmailResponse>>;
139
269
  /** Retrieves an email with its current status and event history. */
140
270
  get(id: string): Promise<Result<GetEmailResponse>>;
271
+ /**
272
+ * Lists your emails, newest first, optionally filtered by tags, status or
273
+ * batch. Pass `next_cursor` back as `cursor` to page through the results.
274
+ */
275
+ list(options?: ListEmailsOptions): Promise<Result<ListEmailsResponse>>;
276
+ /** Every distinct tag on your emails, with a count of emails carrying it. */
277
+ tags(): Promise<Result<ListEmailTagsResponse>>;
141
278
  }
142
279
 
143
280
  declare class Sms {
@@ -149,15 +286,29 @@ declare class Sms {
149
286
  get(id: string): Promise<Result<GetSmsResponse>>;
150
287
  }
151
288
 
289
+ declare class Whatsapp {
290
+ private readonly client;
291
+ constructor(client: Retransmit);
292
+ /**
293
+ * Queues a single WhatsApp message to one recipient. Poll `get(id)` or
294
+ * subscribe to webhooks for the outcome; replies arrive as
295
+ * `whatsapp.received` webhooks.
296
+ */
297
+ send(options: SendWhatsappOptions): Promise<Result<SendWhatsappResponse>>;
298
+ /** Retrieves a WhatsApp message with its current status and event history. */
299
+ get(id: string): Promise<Result<GetWhatsappResponse>>;
300
+ }
301
+
152
302
  declare class Retransmit {
153
303
  readonly emails: Emails;
154
304
  readonly sms: Sms;
305
+ readonly whatsapp: Whatsapp;
155
306
  readonly batch: Batch;
156
307
  private readonly apiKey;
157
308
  private readonly baseUrl;
158
309
  constructor(apiKey?: string, options?: RetransmitOptions);
159
310
  /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
160
- request<T>(method: "GET" | "POST", path: string, body?: unknown): Promise<Result<T>>;
311
+ request<T>(method: "GET" | "POST", path: string, body?: unknown, query?: Record<string, string | number | string[] | undefined>): Promise<Result<T>>;
161
312
  }
162
313
 
163
- export { Batch, EMAIL_STATUSES, type EmailEvent, type EmailStatus, Emails, type GetBatchResponse, type GetEmailResponse, type GetSmsResponse, type Result, Retransmit, type RetransmitError, type RetransmitOptions, SMS_STATUSES, type SendBatchResponse, type SendEmailOptions, type SendEmailResponse, type SendSmsOptions, type SendSmsResponse, Sms, type SmsEvent, type SmsStatus };
314
+ 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 };
package/dist/index.js CHANGED
@@ -9,7 +9,8 @@ function toWirePayload(options) {
9
9
  subject: options.subject,
10
10
  html: options.html,
11
11
  text: options.text,
12
- marketing: options.marketing
12
+ marketing: options.marketing,
13
+ tags: options.tags
13
14
  };
14
15
  }
15
16
  var Emails = class {
@@ -25,6 +26,23 @@ var Emails = class {
25
26
  get(id) {
26
27
  return this.client.request("GET", `/v1/emails/${encodeURIComponent(id)}`);
27
28
  }
29
+ /**
30
+ * Lists your emails, newest first, optionally filtered by tags, status or
31
+ * batch. Pass `next_cursor` back as `cursor` to page through the results.
32
+ */
33
+ list(options = {}) {
34
+ return this.client.request("GET", "/v1/emails", void 0, {
35
+ tag: options.tags?.map((tag) => `${tag.name}:${tag.value}`),
36
+ status: options.status,
37
+ batch_id: options.batchId,
38
+ limit: options.limit,
39
+ cursor: options.cursor
40
+ });
41
+ }
42
+ /** Every distinct tag on your emails, with a count of emails carrying it. */
43
+ tags() {
44
+ return this.client.request("GET", "/v1/emails/tags");
45
+ }
28
46
  };
29
47
 
30
48
  // src/batch.ts
@@ -65,15 +83,45 @@ var Sms = class {
65
83
  }
66
84
  };
67
85
 
86
+ // src/whatsapp.ts
87
+ var Whatsapp = class {
88
+ constructor(client) {
89
+ this.client = client;
90
+ }
91
+ client;
92
+ /**
93
+ * Queues a single WhatsApp message to one recipient. Poll `get(id)` or
94
+ * subscribe to webhooks for the outcome; replies arrive as
95
+ * `whatsapp.received` webhooks.
96
+ */
97
+ send(options) {
98
+ return this.client.request("POST", "/v1/whatsapp", {
99
+ from: options.from,
100
+ to: options.to,
101
+ type: options.type,
102
+ text: options.text,
103
+ preview_url: options.previewUrl,
104
+ template: options.template,
105
+ image: options.image,
106
+ document: options.document
107
+ });
108
+ }
109
+ /** Retrieves a WhatsApp message with its current status and event history. */
110
+ get(id) {
111
+ return this.client.request("GET", `/v1/whatsapp/${encodeURIComponent(id)}`);
112
+ }
113
+ };
114
+
68
115
  // src/retransmit.ts
69
116
  var DEFAULT_BASE_URL = "https://api.retransmit.dev";
70
- var USER_AGENT = "retransmit.dev-node/0.1.0";
117
+ var USER_AGENT = "retransmit.dev-node/0.3.0";
71
118
  function readEnv(name) {
72
119
  return typeof process !== "undefined" ? process.env?.[name] : void 0;
73
120
  }
74
121
  var Retransmit = class {
75
122
  emails = new Emails(this);
76
123
  sms = new Sms(this);
124
+ whatsapp = new Whatsapp(this);
77
125
  batch = new Batch(this);
78
126
  apiKey;
79
127
  baseUrl;
@@ -91,10 +139,17 @@ var Retransmit = class {
91
139
  );
92
140
  }
93
141
  /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
94
- async request(method, path, body) {
142
+ async request(method, path, body, query) {
143
+ const params = new URLSearchParams();
144
+ for (const [key, value] of Object.entries(query ?? {})) {
145
+ if (value === void 0) continue;
146
+ for (const item of Array.isArray(value) ? value : [value]) params.append(key, String(item));
147
+ }
148
+ const encoded = params.toString();
149
+ const search = encoded ? `?${encoded}` : "";
95
150
  let response;
96
151
  try {
97
- response = await fetch(`${this.baseUrl}${path}`, {
152
+ response = await fetch(`${this.baseUrl}${path}${search}`, {
98
153
  method,
99
154
  headers: {
100
155
  Authorization: `Bearer ${this.apiKey}`,
@@ -156,11 +211,14 @@ var SMS_STATUSES = [
156
211
  "rejected",
157
212
  "failed"
158
213
  ];
214
+ var WHATSAPP_STATUSES = ["queued", "sent", "delivered", "read", "failed"];
159
215
  export {
160
216
  Batch,
161
217
  EMAIL_STATUSES,
162
218
  Emails,
163
219
  Retransmit,
164
220
  SMS_STATUSES,
165
- Sms
221
+ Sms,
222
+ WHATSAPP_STATUSES,
223
+ Whatsapp
166
224
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "retransmit.dev",
3
- "version": "0.1.2",
4
- "description": "Node.js SDK for the Retransmit email and SMS APIs",
3
+ "version": "0.3.0",
4
+ "description": "Node.js SDK for the Retransmit email, SMS and WhatsApp APIs",
5
5
  "keywords": [
6
6
  "email",
7
7
  "sms",
@@ -9,7 +9,9 @@
9
9
  "retransmit",
10
10
  "ses",
11
11
  "transactional-email",
12
- "transactional-sms"
12
+ "transactional-sms",
13
+ "whatsapp",
14
+ "whatsapp-cloud-api"
13
15
  ],
14
16
  "homepage": "https://docs.retransmit.dev",
15
17
  "repository": {