@agentisend/sdk-node 0.2.0 → 0.4.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/dist/client.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { AgentiSendError, AgentiSendTransportError, } from './errors.js';
2
+ import { SDK_VERSION } from './version.js';
2
3
  export const DEFAULT_BASE_URL = 'https://api.agentisend.com';
3
4
  /**
4
5
  * Build an idempotency key in the shape that debugs well: `<what>/<which>`,
@@ -78,12 +79,13 @@ export class AgentiSend {
78
79
  }
79
80
  this.fetchImpl = resolved;
80
81
  this.timeoutMs = options.timeoutMs ?? 30_000;
81
- this.userAgent = options.userAgent ?? 'agentisend-node';
82
+ // DX-14: the version travels, so support can tell 0.2.0 from 0.3.0.
83
+ this.userAgent = options.userAgent ?? `agentisend-node/${SDK_VERSION} node/${process.versions.node}`;
82
84
  }
83
- async request(raw) {
85
+ send(raw, accept) {
84
86
  const headers = {
85
87
  authorization: `Bearer ${this.apiKey}`,
86
- accept: 'application/json',
88
+ accept,
87
89
  'user-agent': this.userAgent,
88
90
  };
89
91
  if (raw.body !== undefined)
@@ -91,14 +93,19 @@ export class AgentiSend {
91
93
  if (raw.options?.idempotencyKey) {
92
94
  headers['idempotency-key'] = raw.options.idempotencyKey;
93
95
  }
94
- const response = await this.fetchImpl(`${this.baseUrl}${raw.path}${buildQuery(raw.query)}`, {
96
+ if (raw.options?.headers) {
97
+ Object.assign(headers, raw.options.headers);
98
+ }
99
+ return this.fetchImpl(`${this.baseUrl}${raw.path}${buildQuery(raw.query)}`, {
95
100
  method: raw.method,
96
101
  headers,
97
102
  ...(raw.body !== undefined ? { body: JSON.stringify(raw.body) } : {}),
98
103
  signal: raw.options?.signal ?? AbortSignal.timeout(this.timeoutMs),
99
104
  });
105
+ }
106
+ async request(raw) {
107
+ const response = await this.send(raw, 'application/json');
100
108
  const text = await response.text();
101
- const requestId = response.headers.get('x-request-id') ?? undefined;
102
109
  if (response.status === 204 || text.length === 0) {
103
110
  if (response.status >= 400) {
104
111
  throw new AgentiSendTransportError(`Request failed with ${response.status} and an empty body`, response.status, '');
@@ -112,19 +119,48 @@ export class AgentiSend {
112
119
  catch {
113
120
  throw new AgentiSendTransportError(`Response was not JSON (${response.status})`, response.status, text.slice(0, 500));
114
121
  }
122
+ if (response.status >= 400)
123
+ this.fail(response, parsed, text);
124
+ return parsed;
125
+ }
126
+ /**
127
+ * A response that is not JSON: an attachment, an .eml, the CSV export. A
128
+ * refusal still arrives as the JSON error envelope and throws the same
129
+ * AgentiSendError that request() throws.
130
+ */
131
+ async requestBytes(raw) {
132
+ const response = await this.send(raw, '*/*');
115
133
  if (response.status >= 400) {
116
- if (!isErrorPayload(parsed)) {
117
- throw new AgentiSendTransportError(`Unexpected error shape (${response.status})`, response.status, text.slice(0, 500));
134
+ const text = await response.text();
135
+ let parsed;
136
+ try {
137
+ parsed = JSON.parse(text);
138
+ }
139
+ catch {
140
+ throw new AgentiSendTransportError(`Request failed with ${response.status}`, response.status, text.slice(0, 500));
118
141
  }
119
- const retryAfter = response.headers.get('retry-after');
120
- const rateLimit = readRateLimit(response);
121
- throw new AgentiSendError(parsed.error, response.status, {
122
- ...(retryAfter ? { retryAfterSeconds: Number(retryAfter) } : {}),
123
- ...(requestId ? { requestId } : {}),
124
- ...(rateLimit ? { rateLimit } : {}),
125
- });
142
+ this.fail(response, parsed, text);
126
143
  }
127
- return parsed;
144
+ if (response.arrayBuffer)
145
+ return new Uint8Array(await response.arrayBuffer());
146
+ return new TextEncoder().encode(await response.text());
147
+ }
148
+ /** The same download, decoded as UTF-8: an .eml, raw MIME, a CSV. */
149
+ async requestText(raw) {
150
+ return new TextDecoder().decode(await this.requestBytes(raw));
151
+ }
152
+ fail(response, parsed, text) {
153
+ if (!isErrorPayload(parsed)) {
154
+ throw new AgentiSendTransportError(`Unexpected error shape (${response.status})`, response.status, text.slice(0, 500));
155
+ }
156
+ const requestId = response.headers.get('x-request-id') ?? undefined;
157
+ const retryAfter = response.headers.get('retry-after');
158
+ const rateLimit = readRateLimit(response);
159
+ throw new AgentiSendError(parsed.error, response.status, {
160
+ ...(retryAfter ? { retryAfterSeconds: Number(retryAfter) } : {}),
161
+ ...(requestId ? { requestId } : {}),
162
+ ...(rateLimit ? { rateLimit } : {}),
163
+ });
128
164
  }
129
165
  emails = {
130
166
  send: (payload, options) => this.request({ method: 'POST', path: '/emails', body: payload, ...(options ? { options } : {}) }),
@@ -155,11 +191,20 @@ export class AgentiSend {
155
191
  path: `/emails/${id}/cancel`,
156
192
  ...(options ? { options } : {}),
157
193
  }),
194
+ /** Delete a sent email and its events for good. A scheduled one must be cancelled first. */
195
+ remove: (id, options) => this.request({
196
+ method: 'DELETE',
197
+ path: `/emails/${id}`,
198
+ ...(options ? { options } : {}),
199
+ }),
158
200
  /** Move an email to a new time. Works from scheduled AND canceled. */
159
201
  reschedule: (id, scheduledAt, options) => this.request({
160
202
  method: 'POST',
161
203
  path: `/emails/${id}/reschedule`,
162
- body: { scheduled_at: scheduledAt },
204
+ body: {
205
+ scheduled_at: scheduledAt,
206
+ ...(options?.timezone !== undefined ? { timezone: options.timezone } : {}),
207
+ },
163
208
  ...(options ? { options } : {}),
164
209
  }),
165
210
  /**
@@ -170,6 +215,33 @@ export class AgentiSend {
170
215
  explain: (id) => this.request({ method: 'GET', path: `/emails/${id}/explain` }),
171
216
  /** Run every gate without sending or spending. Free, always. */
172
217
  preflight: (payload) => this.request({ method: 'POST', path: '/emails/preflight', body: payload }),
218
+ /** Deliverability lint without sending: a 0-100 placement score and every finding with its fix. */
219
+ lint: (payload) => this.request({ method: 'POST', path: '/emails/lint', body: payload }),
220
+ /** Every event recorded for one message, oldest first. */
221
+ events: (id, query = {}) => this.request({
222
+ method: 'GET',
223
+ path: `/emails/${id}/events`,
224
+ query: query,
225
+ }),
226
+ /** The exact RFC 5322 source of this message, as text. */
227
+ mime: (id) => this.requestText({ method: 'GET', path: `/emails/${id}/mime` }),
228
+ /** The same message as a .eml file. */
229
+ eml: (id) => this.requestText({ method: 'GET', path: `/emails/${id}/eml` }),
230
+ /** One attachment's bytes, as they were sent. */
231
+ attachment: (id, attachmentId) => this.requestBytes({ method: 'GET', path: `/emails/${id}/attachments/${attachmentId}` }),
232
+ /** Cancel scheduled or queued messages by id, or everything scheduled before a time. */
233
+ bulkCancel: (payload, options) => this.request({
234
+ method: 'POST',
235
+ path: '/emails/bulk-cancel',
236
+ body: payload,
237
+ ...(options ? { options } : {}),
238
+ }),
239
+ /** The message log as CSV, with the same filters as list. */
240
+ export: (query = {}) => this.requestText({
241
+ method: 'GET',
242
+ path: '/emails/export.csv',
243
+ query: query,
244
+ }),
173
245
  /**
174
246
  * Move a scheduled email. The same act as `reschedule`, under the verb a
175
247
  * Resend integration already calls (M5.5).
@@ -196,6 +268,16 @@ export class AgentiSend {
196
268
  ...(options ? { options } : {}),
197
269
  }),
198
270
  delete: (id, options) => this.request({ method: 'DELETE', path: `/api-keys/${id}`, ...(options ? { options } : {}) }),
271
+ /**
272
+ * Issue a new token for this key. The old one keeps working for
273
+ * `grace_hours` (0, 1 or 24) so a deploy can pick up the new one.
274
+ */
275
+ rotate: (id, payload, options) => this.request({
276
+ method: 'POST',
277
+ path: `/api-keys/${id}/rotate`,
278
+ body: payload,
279
+ ...(options ? { options } : {}),
280
+ }),
199
281
  };
200
282
  domains = {
201
283
  create: (payload, options) => this.request({ method: 'POST', path: '/domains', body: payload, ...(options ? { options } : {}) }),
@@ -205,8 +287,10 @@ export class AgentiSend {
205
287
  query: query,
206
288
  }),
207
289
  get: (id) => this.request({ method: 'GET', path: `/domains/${id}` }),
290
+ /** One-shot DNS sheet: zone file, copy-paste commands, provider. */
291
+ setup: (id) => this.request({ method: 'GET', path: `/domains/${id}/setup` }),
208
292
  verify: (id, options) => this.request({ method: 'POST', path: `/domains/${id}/verify`, ...(options ? { options } : {}) }),
209
- /** A8 + P3b: click/open tracking, or a new tracking_subdomain. Never removed. */
293
+ /** A8 + P3b: click/open tracking, TLS mode, or a new tracking_subdomain. Never removed. */
210
294
  update: (id, payload, options) => this.request({
211
295
  method: 'PATCH',
212
296
  path: `/domains/${id}`,
@@ -214,6 +298,24 @@ export class AgentiSend {
214
298
  ...(options ? { options } : {}),
215
299
  }),
216
300
  delete: (id, options) => this.request({ method: 'DELETE', path: `/domains/${id}`, ...(options ? { options } : {}) }),
301
+ /**
302
+ * Long-poll until the domain is verified or failed, or `timeout` seconds
303
+ * (0-25) pass; 0 is a snapshot. It does not re-check DNS: the server checks
304
+ * on its own, and `next_poll_seconds` says when to ask again.
305
+ */
306
+ wait: (id, query = {}) => this.request({
307
+ method: 'GET',
308
+ path: `/domains/${id}/wait`,
309
+ query: query,
310
+ }),
311
+ /** Newest-first timeline: each DNS check, each record found or lost, verified, deleted. */
312
+ events: (id, query = {}) => this.request({
313
+ method: 'GET',
314
+ path: `/domains/${id}/events`,
315
+ query: query,
316
+ }),
317
+ /** Alias of setup: detects the DNS provider and returns the exact records to add. */
318
+ connect: (id) => this.request({ method: 'GET', path: `/domains/${id}/connect` }),
217
319
  };
218
320
  webhooks = {
219
321
  create: (payload, options) => this.request({ method: 'POST', path: '/webhooks', body: payload, ...(options ? { options } : {}) }),
@@ -244,6 +346,18 @@ export class AgentiSend {
244
346
  path: `/webhooks/${id}/dead-letters`,
245
347
  query: query,
246
348
  }),
349
+ /** Every delivery to this endpoint, with each attempt and what the receiver answered. */
350
+ deliveries: (id, query = {}) => this.request({
351
+ method: 'GET',
352
+ path: `/webhooks/${id}/deliveries`,
353
+ query: query,
354
+ }),
355
+ /** A new signing secret, returned once. The previous one keeps verifying for 24 hours. */
356
+ rotateSecret: (id, options) => this.request({
357
+ method: 'POST',
358
+ path: `/webhooks/${id}/rotate-secret`,
359
+ ...(options ? { options } : {}),
360
+ }),
247
361
  };
248
362
  limits = {
249
363
  list: (query = {}) => this.request({
@@ -270,6 +384,16 @@ export class AgentiSend {
270
384
  path: `/limits/keys/${apiKeyId}/resume`,
271
385
  ...(options ? { options } : {}),
272
386
  }),
387
+ /**
388
+ * Stop every key on the account sending, in one call. Undoing it is a
389
+ * person's decision, made in the console; no key can resume it.
390
+ */
391
+ killAll: (reason, options) => this.request({
392
+ method: 'POST',
393
+ path: '/limits/kill-all',
394
+ body: reason !== undefined ? { reason } : {},
395
+ ...(options ? { options } : {}),
396
+ }),
273
397
  };
274
398
  /** M5.5 — the rest of the verb surface a Resend integration calls. */
275
399
  /**
@@ -295,6 +419,12 @@ export class AgentiSend {
295
419
  query: query,
296
420
  }),
297
421
  get: (id) => this.request({ method: 'GET', path: `/emails/receiving/${id}` }),
422
+ /** Delete one received email for good. */
423
+ remove: (id, options) => this.request({
424
+ method: 'DELETE',
425
+ path: `/emails/receiving/${id}`,
426
+ ...(options ? { options } : {}),
427
+ }),
298
428
  threads: (query = {}) => this.request({
299
429
  method: 'GET',
300
430
  path: '/emails/receiving/threads',
@@ -307,6 +437,13 @@ export class AgentiSend {
307
437
  ...(options ? { options } : {}),
308
438
  }),
309
439
  attachments: (id) => this.request({ method: 'GET', path: `/emails/receiving/${id}/attachments` }),
440
+ /** One received attachment's bytes, served as an inert download. */
441
+ attachment: (id, attachmentId) => this.requestBytes({
442
+ method: 'GET',
443
+ path: `/emails/receiving/${id}/attachments/${attachmentId}`,
444
+ }),
445
+ /** The message exactly as it arrived, headers and all. */
446
+ raw: (id) => this.requestText({ method: 'GET', path: `/emails/receiving/${id}/raw` }),
310
447
  };
311
448
  /**
312
449
  * M5.7 — the custom-event registry. Declaring an event's fields turns a
@@ -373,6 +510,65 @@ export class AgentiSend {
373
510
  body: payload,
374
511
  ...(options ? { options } : {}),
375
512
  }),
513
+ /** Up to 1,000 contacts. Each item succeeds or fails on its own. */
514
+ batch: (payload, options) => this.request({ method: 'POST', path: '/contacts/batch', body: payload, ...(options ? { options } : {}) }),
515
+ /** CSV in `csv`. Finishes in this response. `on_conflict` defaults to skip. */
516
+ importCsv: (payload, options) => this.request({ method: 'POST', path: '/contacts/imports', body: payload, ...(options ? { options } : {}) }),
517
+ /** Segments whose rules include this contact. The id may be an email. */
518
+ segments: (id) => this.request({ method: 'GET', path: `/contacts/${id}/segments` }),
519
+ /** Refused: membership is the segment's rules. Names PATCH /segments/:id. */
520
+ addSegment: (id, segmentId, options) => this.request({
521
+ method: 'POST',
522
+ path: `/contacts/${id}/segments/${segmentId}`,
523
+ ...(options ? { options } : {}),
524
+ }),
525
+ /** Refused: membership is the segment's rules. Names PATCH /segments/:id. */
526
+ removeSegment: (id, segmentId, options) => this.request({
527
+ method: 'DELETE',
528
+ path: `/contacts/${id}/segments/${segmentId}`,
529
+ ...(options ? { options } : {}),
530
+ }),
531
+ /** Lift a marketing unsubscribe after the person opts back in. */
532
+ resubscribe: (id, payload, options) => this.request({
533
+ method: 'POST',
534
+ path: `/contacts/${id}/resubscribe`,
535
+ body: payload,
536
+ ...(options ? { options } : {}),
537
+ }),
538
+ };
539
+ /** Property names and types already stored on the account. */
540
+ contactProperties = {
541
+ list: () => this.request({ method: 'GET', path: '/contact-properties' }),
542
+ get: (id) => this.request({ method: 'GET', path: `/contact-properties/${id}` }),
543
+ };
544
+ /**
545
+ * The same contact list, at the paths the official SDK uses when a call
546
+ * names an audience. This account has one contact list.
547
+ */
548
+ audiences = {
549
+ createContact: (audienceId, payload, options) => this.request({
550
+ method: 'POST',
551
+ path: `/audiences/${audienceId}/contacts`,
552
+ body: payload,
553
+ ...(options ? { options } : {}),
554
+ }),
555
+ listContacts: (audienceId, query = {}) => this.request({
556
+ method: 'GET',
557
+ path: `/audiences/${audienceId}/contacts`,
558
+ query: query,
559
+ }),
560
+ getContact: (audienceId, id) => this.request({ method: 'GET', path: `/audiences/${audienceId}/contacts/${id}` }),
561
+ updateContact: (audienceId, id, payload, options) => this.request({
562
+ method: 'PATCH',
563
+ path: `/audiences/${audienceId}/contacts/${id}`,
564
+ body: payload,
565
+ ...(options ? { options } : {}),
566
+ }),
567
+ deleteContact: (audienceId, id, options) => this.request({
568
+ method: 'DELETE',
569
+ path: `/audiences/${audienceId}/contacts/${id}`,
570
+ ...(options ? { options } : {}),
571
+ }),
376
572
  };
377
573
  broadcasts = {
378
574
  create: (payload, options) => this.request({ method: 'POST', path: '/broadcasts', body: payload, ...(options ? { options } : {}) }),
@@ -382,8 +578,23 @@ export class AgentiSend {
382
578
  query: query,
383
579
  }),
384
580
  get: (id) => this.request({ method: 'GET', path: `/broadcasts/${id}` }),
581
+ /** Sent, delivered, opened, clicked, bounced, complained and unsubscribed. A test send is not included. */
582
+ stats: (id) => this.request({ method: 'GET', path: `/broadcasts/${id}/stats` }),
583
+ /** The rendered message. Nothing is sent. */
584
+ preview: (id) => this.request({ method: 'GET', path: `/broadcasts/${id}/preview` }),
385
585
  update: (id, payload, options) => this.request({ method: 'PATCH', path: `/broadcasts/${id}`, body: payload, ...(options ? { options } : {}) }),
386
- send: (id, options) => this.request({ method: 'POST', path: `/broadcasts/${id}/send`, ...(options ? { options } : {}) }),
586
+ /**
587
+ * Send now, or pass `scheduledAt` to schedule it instead (the same time
588
+ * grammar PATCH /broadcasts/:id accepts). Returns 200 when the broadcast
589
+ * has finished sending or was scheduled, and 202 while it is still
590
+ * sending. A 202 has status sending.
591
+ */
592
+ send: (id, payload, options) => this.request({
593
+ method: 'POST',
594
+ path: `/broadcasts/${id}/send`,
595
+ ...(payload?.scheduledAt !== undefined ? { body: { scheduled_at: payload.scheduledAt } } : {}),
596
+ ...(options ? { options } : {}),
597
+ }),
387
598
  /** Archive, not delete: a sent broadcast is history worth keeping (F14). */
388
599
  archive: (id, options) => this.request({ method: 'POST', path: `/broadcasts/${id}/archive`, ...(options ? { options } : {}) }),
389
600
  messages: (id, query = {}) => this.request({
@@ -391,7 +602,7 @@ export class AgentiSend {
391
602
  path: `/broadcasts/${id}/messages`,
392
603
  query: query,
393
604
  }),
394
- /** Cancel a scheduled broadcast: it returns to draft, editable again. */
605
+ /** Cancel a scheduled broadcast, or stop one that is still sending. Mail already handed off is not pulled back. A scheduled broadcast returns to draft. */
395
606
  cancel: (id, options) => this.request({
396
607
  method: 'POST',
397
608
  path: `/broadcasts/${id}/cancel`,
@@ -409,12 +620,40 @@ export class AgentiSend {
409
620
  update: (id, payload, options) => this.request({ method: 'PATCH', path: `/templates/${id}`, body: payload, ...(options ? { options } : {}) }),
410
621
  /** Snapshot the draft as an immutable version. Nothing already sent changes. */
411
622
  publish: (id, options) => this.request({ method: 'POST', path: `/templates/${id}/publish`, ...(options ? { options } : {}) }),
412
- render: (id, values = {}, versionNumber) => this.request({
623
+ /**
624
+ * `rendered` is the HTML; `text` is the plain-text part a markdown
625
+ * template yields. `preview: true` shows a missing required variable as
626
+ * `{{name}}` and lists it in `missing_variables` instead of failing.
627
+ */
628
+ render: (id, values = {}, versionNumber, options = {}) => this.request({
413
629
  method: 'POST',
414
630
  path: `/templates/${id}/render`,
415
- body: { values, ...(versionNumber !== undefined ? { version_number: versionNumber } : {}) },
631
+ body: {
632
+ values,
633
+ ...(versionNumber !== undefined ? { version_number: versionNumber } : {}),
634
+ ...(options.preview !== undefined ? { preview: options.preview } : {}),
635
+ },
416
636
  }),
637
+ /**
638
+ * Delete a template and its versions. Sent mail keeps its body and its
639
+ * template reference. Refused while a broadcast that has not been sent
640
+ * still uses it.
641
+ */
642
+ delete: (id, options) => this.request({ method: 'DELETE', path: `/templates/${id}`, ...(options ? { options } : {}) }),
417
643
  versions: (id) => this.request({ method: 'GET', path: `/templates/${id}/versions` }),
644
+ version: (id, versionNumber) => this.request({ method: 'GET', path: `/templates/${id}/versions/${versionNumber}` }),
645
+ /** Structured diff between two versions, or between a version and the draft. */
646
+ diff: (id, from, to) => this.request({ method: 'GET', path: `/templates/${id}/diff`, query: { from, to } }),
647
+ /**
648
+ * Start a new draft from an older version. History is never rewritten; the
649
+ * live version stays until that draft is published.
650
+ */
651
+ rollback: (id, toVersion, options) => this.request({
652
+ method: 'POST',
653
+ path: `/templates/${id}/rollback`,
654
+ body: { to_version: toVersion },
655
+ ...(options ? { options } : {}),
656
+ }),
418
657
  /** Copy a template into a fresh editable draft, history not included. */
419
658
  duplicate: (id, payload = {}, options) => this.request({
420
659
  method: 'POST',
@@ -430,6 +669,8 @@ export class AgentiSend {
430
669
  query: query,
431
670
  }),
432
671
  add: (payload, options) => this.request({ method: 'POST', path: '/suppressions', body: payload, ...(options ? { options } : {}) }),
672
+ /** The id or the address. */
673
+ get: (id, options) => this.request({ method: 'GET', path: `/suppressions/${id}`, ...(options ? { options } : {}) }),
433
674
  remove: (id, options) => this.request({ method: 'DELETE', path: `/suppressions/${id}`, ...(options ? { options } : {}) }),
434
675
  /** Up to 500 at a time. Each item succeeds or fails on its own. */
435
676
  batchAdd: (emails, level, options) => this.request({
@@ -459,6 +700,12 @@ export class AgentiSend {
459
700
  get: (id) => this.request({ method: 'GET', path: `/segments/${id}` }),
460
701
  update: (id, payload, options) => this.request({ method: 'PATCH', path: `/segments/${id}`, body: payload, ...(options ? { options } : {}) }),
461
702
  delete: (id, options) => this.request({ method: 'DELETE', path: `/segments/${id}`, ...(options ? { options } : {}) }),
703
+ /** The account contact list. An audience id on this path does not select a second list. */
704
+ contacts: (id, query = {}) => this.request({
705
+ method: 'GET',
706
+ path: `/segments/${id}/contacts`,
707
+ query: query,
708
+ }),
462
709
  members: (id) => this.request({ method: 'GET', path: `/segments/${id}/members` }),
463
710
  };
464
711
  /**
@@ -543,9 +790,120 @@ export class AgentiSend {
543
790
  };
544
791
  /** Public per-component health — the same document the status page renders. */
545
792
  status = () => this.request({ method: 'GET', path: '/status' });
793
+ /** Daily uptime per component over the last 90 days, or `days`. A day nobody measured has no samples. */
794
+ statusHistory = (query = {}) => this.request({ method: 'GET', path: '/status/history', query });
795
+ /**
796
+ * Sending-domain DNS check. No API key. Ten checks a minute from one
797
+ * network. Returns the records to publish; stores nothing.
798
+ */
799
+ dnsCheck = (domain) => this.request({ method: 'GET', path: `/tools/dns/${encodeURIComponent(domain)}` });
800
+ /** Send, delivery and engagement figures with a daily series, over a window. */
801
+ metrics = (query = {}) => this.request({
802
+ method: 'GET',
803
+ path: '/metrics',
804
+ query: query,
805
+ });
806
+ /** This period's usage per key: budget, consumed, remaining, rate window, last used. */
807
+ usage = () => this.request({ method: 'GET', path: '/usage' });
808
+ account = {
809
+ /** Lifecycle status, the reason for it, and when the account was created. */
810
+ get: () => this.request({ method: 'GET', path: '/account' }),
811
+ /**
812
+ * Set the postal address printed in every broadcast footer. `null` clears
813
+ * it. A broadcast is not sent until the account has one.
814
+ */
815
+ update: (payload, options) => this.request({
816
+ method: 'PATCH',
817
+ path: '/account',
818
+ body: payload,
819
+ ...(options ? { options } : {}),
820
+ }),
821
+ };
546
822
  trust = {
547
823
  /** What the platform currently thinks of this account, in a shape an agent can branch on. */
548
824
  standing: () => this.request({ method: 'GET', path: '/trust/standing' }),
825
+ /** The published enforcement thresholds, as numbers. */
826
+ thresholds: () => this.request({ method: 'GET', path: '/trust/thresholds' }),
827
+ /** Appeal the current standing. The reason is recorded verbatim; a person answers by `sla_deadline_at`. */
828
+ appeal: (reason, options) => this.request({
829
+ method: 'POST',
830
+ path: '/trust/appeal',
831
+ body: { reason },
832
+ ...(options ? { options } : {}),
833
+ }),
834
+ /** The remediation checklist, with what has been ticked. */
835
+ remediation: () => this.request({ method: 'GET', path: '/trust/remediation' }),
836
+ /** Tick or untick one checklist item. Returns the whole checklist. */
837
+ setRemediation: (payload, options) => this.request({
838
+ method: 'POST',
839
+ path: '/trust/remediation',
840
+ body: payload,
841
+ ...(options ? { options } : {}),
842
+ }),
843
+ };
844
+ /**
845
+ * Sends held for a person to approve, and what they decided. Approving or
846
+ * rejecting is done in the console; a key can list, never decide.
847
+ */
848
+ agentActions = {
849
+ list: (query = {}) => this.request({
850
+ method: 'GET',
851
+ path: '/agent-actions',
852
+ query: query,
853
+ }),
854
+ };
855
+ /** Conditions this account should know about: a spent budget, a kill switch, a lost domain. */
856
+ notifications = {
857
+ list: (query = {}) => this.request({
858
+ method: 'GET',
859
+ path: '/notifications',
860
+ query: query,
861
+ }),
862
+ read: (id, options) => this.request({
863
+ method: 'POST',
864
+ path: `/notifications/${id}/read`,
865
+ ...(options ? { options } : {}),
866
+ }),
867
+ readAll: (options) => this.request({ method: 'POST', path: '/notifications/read', ...(options ? { options } : {}) }),
868
+ preferences: () => this.request({ method: 'GET', path: '/notifications/preferences' }),
869
+ setPreferences: (preferences, options) => this.request({
870
+ method: 'PATCH',
871
+ path: '/notifications/preferences',
872
+ body: { preferences },
873
+ ...(options ? { options } : {}),
874
+ }),
875
+ };
876
+ dedicatedIps = {
877
+ list: (query = {}) => this.request({
878
+ method: 'GET',
879
+ path: '/dedicated-ips',
880
+ query: query,
881
+ }),
882
+ /** Provision a dedicated IP. It starts warming on the published curve. */
883
+ provision: (payload, options) => this.request({
884
+ method: 'POST',
885
+ path: '/dedicated-ips',
886
+ body: payload,
887
+ ...(options ? { options } : {}),
888
+ }),
889
+ release: (id, options) => this.request({ method: 'DELETE', path: `/dedicated-ips/${id}`, ...(options ? { options } : {}) }),
890
+ /** The published warmup curve: how volume moves, and when. */
891
+ ramp: () => this.request({ method: 'GET', path: '/dedicated-ips/ramp' }),
892
+ /** Which route this message would take right now, by the same math the send path uses. */
893
+ routeDecision: (messageId) => this.request({ method: 'GET', path: `/dedicated-ips/route-decision/${messageId}` }),
894
+ };
895
+ deliverability = {
896
+ /** Every sending domain with its standing over the window, worst first. */
897
+ domains: (query = {}) => this.request({ method: 'GET', path: '/deliverability/domains', query }),
898
+ domain: (id) => this.request({ method: 'GET', path: `/deliverability/domains/${id}` }),
899
+ /** Persist today's rates as a snapshot. Idempotent per domain per day. */
900
+ snapshot: (id, options) => this.request({
901
+ method: 'POST',
902
+ path: `/deliverability/domains/${id}/snapshot`,
903
+ ...(options ? { options } : {}),
904
+ }),
905
+ /** Aggregate authentication reports: aligned and failing volume per day, and every address sending as you. */
906
+ dmarc: (query = {}) => this.request({ method: 'GET', path: '/deliverability/dmarc', query }),
549
907
  };
550
908
  billing = {
551
909
  /**
@@ -554,6 +912,8 @@ export class AgentiSend {
554
912
  * budget comes from Starter; only a signed-in human in the console can buy.
555
913
  */
556
914
  plan: () => this.request({ method: 'GET', path: '/billing/plan' }),
915
+ /** Plan, usage against each published limit, and what the next tier changes. Buying stays in the console. */
916
+ get: () => this.request({ method: 'GET', path: '/billing' }),
557
917
  };
558
918
  }
559
919
  //# sourceMappingURL=client.js.map