retransmit.dev 0.2.0 → 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
@@ -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
package/dist/index.cjs CHANGED
@@ -42,7 +42,8 @@ function toWirePayload(options) {
42
42
  subject: options.subject,
43
43
  html: options.html,
44
44
  text: options.text,
45
- marketing: options.marketing
45
+ marketing: options.marketing,
46
+ tags: options.tags
46
47
  };
47
48
  }
48
49
  var Emails = class {
@@ -58,6 +59,23 @@ var Emails = class {
58
59
  get(id) {
59
60
  return this.client.request("GET", `/v1/emails/${encodeURIComponent(id)}`);
60
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
+ }
61
79
  };
62
80
 
63
81
  // src/batch.ts
@@ -129,7 +147,7 @@ var Whatsapp = class {
129
147
 
130
148
  // src/retransmit.ts
131
149
  var DEFAULT_BASE_URL = "https://api.retransmit.dev";
132
- var USER_AGENT = "retransmit.dev-node/0.1.0";
150
+ var USER_AGENT = "retransmit.dev-node/0.3.0";
133
151
  function readEnv(name) {
134
152
  return typeof process !== "undefined" ? process.env?.[name] : void 0;
135
153
  }
@@ -154,10 +172,17 @@ var Retransmit = class {
154
172
  );
155
173
  }
156
174
  /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
157
- 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}` : "";
158
183
  let response;
159
184
  try {
160
- response = await fetch(`${this.baseUrl}${path}`, {
185
+ response = await fetch(`${this.baseUrl}${path}${search}`, {
161
186
  method,
162
187
  headers: {
163
188
  Authorization: `Bearer ${this.apiKey}`,
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 {
@@ -213,6 +268,13 @@ declare class Emails {
213
268
  send(options: SendEmailOptions): Promise<Result<SendEmailResponse>>;
214
269
  /** Retrieves an email with its current status and event history. */
215
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>>;
216
278
  }
217
279
 
218
280
  declare class Sms {
@@ -246,7 +308,7 @@ declare class Retransmit {
246
308
  private readonly baseUrl;
247
309
  constructor(apiKey?: string, options?: RetransmitOptions);
248
310
  /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
249
- 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>>;
250
312
  }
251
313
 
252
- export { Batch, EMAIL_STATUSES, type EmailEvent, type EmailStatus, Emails, type GetBatchResponse, type GetEmailResponse, type GetSmsResponse, type GetWhatsappResponse, 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 };
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 {
@@ -213,6 +268,13 @@ declare class Emails {
213
268
  send(options: SendEmailOptions): Promise<Result<SendEmailResponse>>;
214
269
  /** Retrieves an email with its current status and event history. */
215
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>>;
216
278
  }
217
279
 
218
280
  declare class Sms {
@@ -246,7 +308,7 @@ declare class Retransmit {
246
308
  private readonly baseUrl;
247
309
  constructor(apiKey?: string, options?: RetransmitOptions);
248
310
  /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
249
- 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>>;
250
312
  }
251
313
 
252
- export { Batch, EMAIL_STATUSES, type EmailEvent, type EmailStatus, Emails, type GetBatchResponse, type GetEmailResponse, type GetSmsResponse, type GetWhatsappResponse, 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 };
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
@@ -96,7 +114,7 @@ var Whatsapp = class {
96
114
 
97
115
  // src/retransmit.ts
98
116
  var DEFAULT_BASE_URL = "https://api.retransmit.dev";
99
- var USER_AGENT = "retransmit.dev-node/0.1.0";
117
+ var USER_AGENT = "retransmit.dev-node/0.3.0";
100
118
  function readEnv(name) {
101
119
  return typeof process !== "undefined" ? process.env?.[name] : void 0;
102
120
  }
@@ -121,10 +139,17 @@ var Retransmit = class {
121
139
  );
122
140
  }
123
141
  /** Internal transport shared by the resource classes. API failures are returned, never thrown. */
124
- 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}` : "";
125
150
  let response;
126
151
  try {
127
- response = await fetch(`${this.baseUrl}${path}`, {
152
+ response = await fetch(`${this.baseUrl}${path}${search}`, {
128
153
  method,
129
154
  headers: {
130
155
  Authorization: `Bearer ${this.apiKey}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "retransmit.dev",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Node.js SDK for the Retransmit email, SMS and WhatsApp APIs",
5
5
  "keywords": [
6
6
  "email",