@sentdm/sentdm 0.38.0 → 0.40.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/client.d.mts +4 -2
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +4 -2
  5. package/client.d.ts.map +1 -1
  6. package/client.js +2 -0
  7. package/client.js.map +1 -1
  8. package/client.mjs +2 -0
  9. package/client.mjs.map +1 -1
  10. package/package.json +1 -1
  11. package/resources/conversations.d.mts +71 -3
  12. package/resources/conversations.d.mts.map +1 -1
  13. package/resources/conversations.d.ts +71 -3
  14. package/resources/conversations.d.ts.map +1 -1
  15. package/resources/index.d.mts +1 -1
  16. package/resources/index.d.mts.map +1 -1
  17. package/resources/index.d.ts +1 -1
  18. package/resources/index.d.ts.map +1 -1
  19. package/resources/index.js.map +1 -1
  20. package/resources/index.mjs.map +1 -1
  21. package/resources/messages.d.mts +171 -14
  22. package/resources/messages.d.mts.map +1 -1
  23. package/resources/messages.d.ts +171 -14
  24. package/resources/messages.d.ts.map +1 -1
  25. package/resources/messages.js +21 -3
  26. package/resources/messages.js.map +1 -1
  27. package/resources/messages.mjs +21 -3
  28. package/resources/messages.mjs.map +1 -1
  29. package/resources/templates.d.mts +276 -14
  30. package/resources/templates.d.mts.map +1 -1
  31. package/resources/templates.d.ts +276 -14
  32. package/resources/templates.d.ts.map +1 -1
  33. package/resources/templates.js +28 -2
  34. package/resources/templates.js.map +1 -1
  35. package/resources/templates.mjs +28 -2
  36. package/resources/templates.mjs.map +1 -1
  37. package/resources/webhooks.d.mts +693 -22
  38. package/resources/webhooks.d.mts.map +1 -1
  39. package/resources/webhooks.d.ts +693 -22
  40. package/resources/webhooks.d.ts.map +1 -1
  41. package/src/client.ts +10 -0
  42. package/src/resources/conversations.ts +78 -3
  43. package/src/resources/index.ts +4 -0
  44. package/src/resources/messages.ts +182 -14
  45. package/src/resources/templates.ts +289 -14
  46. package/src/resources/webhooks.ts +764 -22
  47. package/src/version.ts +1 -1
  48. package/version.d.mts +1 -1
  49. package/version.d.ts +1 -1
  50. package/version.js +1 -1
  51. package/version.mjs +1 -1
@@ -1,6 +1,7 @@
1
1
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  import { APIResource } from '../core/resource';
4
+ import * as TemplatesAPI from './templates';
4
5
  import * as WebhooksAPI from './webhooks';
5
6
  import { APIPromise } from '../core/api-promise';
6
7
  import { PagePromise, TemplatesPage, type TemplatesPageParams } from '../core/pagination';
@@ -17,7 +18,9 @@ export class Templates extends APIResource {
17
18
  /**
18
19
  * Creates a new message template with header, body, footer, and buttons. The
19
20
  * template can be submitted for review immediately or saved as draft for later
20
- * submission.
21
+ * submission. There is no `name` field on create — the display name is derived
22
+ * from the template's content and can be changed afterwards with
23
+ * `PUT /v3/templates/{id}`.
21
24
  *
22
25
  * @example
23
26
  * ```ts
@@ -67,7 +70,31 @@ export class Templates extends APIResource {
67
70
 
68
71
  /**
69
72
  * Updates an existing template's name, category, language, definition, or submits
70
- * it for review.
73
+ * it for review. While the template is in review (status PENDING, or any channel
74
+ * awaiting a verdict) its definition, category and language are frozen and a
75
+ * resubmission is refused — those requests answer 409 CONFLICT_006. The display
76
+ * name stays editable throughout.
77
+ *
78
+ * `definition`, `category` and `language` are editable only from status DRAFT,
79
+ * REJECTED or APPROVED. An edit to any of them on a template in another state
80
+ * (PAUSED, DISABLED or REVOKED) is refused with 400 VALIDATION_001 and the detail
81
+ * "Template (except display name) cannot be updated unless it is in draft or
82
+ * rejected status"; `name` stays editable in every state. `submit_for_review` on a
83
+ * PAUSED, DISABLED or REVOKED template is accepted and answers 200, but opens no
84
+ * review and does not move the status — only the reviewer can reinstate it.
85
+ *
86
+ * Editing an APPROVED template is a live edit: the new content is stored
87
+ * immediately, and sending `submit_for_review: true` re-opens review, which
88
+ * returns the affected channels to PENDING so they stop sending until they are
89
+ * approved again. The previously approved content is never sent during re-review.
90
+ * Watch the per-channel `templates` webhook events rather than assuming the
91
+ * template-level status.
92
+ *
93
+ * Templates provisioned by Sent (light-onboarding templates, whose names carry the
94
+ * reserved `sent_` prefix) are read-only: every field is refused with 400
95
+ * VALIDATION*001 and the detail "This template is read-only. Only 'submit for
96
+ * review' is allowed.", and only `submit_for_review` is accepted. A `name`
97
+ * starting with `sent*` is refused for the same reason — the prefix is reserved.
71
98
  *
72
99
  * @example
73
100
  * ```ts
@@ -205,13 +232,40 @@ export interface Template {
205
232
  */
206
233
  id?: string;
207
234
 
235
+ /**
236
+ * Which consent keyword this template answers, when it is one of Sent's
237
+ * auto-replies: OPT_IN, OPT_OUT, HELP, or OTHER for a customer-defined keyword.
238
+ * Null for an ordinary template, and omitted from the response, so its presence is
239
+ * the answer to "is this an auto-reply".
240
+ *
241
+ * Deliberately not required, unlike CustomerId, even though the same "no single
242
+ * mapper" argument applies: NJsonSchema publishes a C# required member in the
243
+ * schema's required array, so the contract would have advertised a field this
244
+ * response omits for every ordinary template, and a generated client could refuse
245
+ * the common case. A compile-time guard is not worth a wrong published contract.
246
+ * Every mapping site sets it explicitly, and TemplateResponseSchemaTests pins the
247
+ * field as optional so it cannot be reintroduced.
248
+ */
249
+ auto_reply_action?: string | null;
250
+
208
251
  /**
209
252
  * Template category: MARKETING, UTILITY, AUTHENTICATION
210
253
  */
211
254
  category?: string;
212
255
 
213
256
  /**
214
- * Supported channels: sms, whatsapp
257
+ * The channels this template's definition can render on, in canonical order: sms,
258
+ * whatsapp, rcs.
259
+ *
260
+ * Derived from the definition's body, mirroring each channel's send-time fallback
261
+ * chain, so a channel is listed only when a real body would be produced for it:
262
+ * SMS reads sms ?? multiChannel, WhatsApp reads whatsapp ?? multiChannel, and RCS
263
+ * reads rcs ?? multiChannel ?? sms. A multiChannel body therefore reports all
264
+ * three, and the extra SMS fallback on RCS is why an sms/whatsapp pair reports RCS
265
+ * too.
266
+ *
267
+ * This says what the content can render on, not what may be sent: sending also
268
+ * needs the template approved for that channel.
215
269
  */
216
270
  channels?: Array<string> | null;
217
271
 
@@ -236,7 +290,8 @@ export interface Template {
236
290
  name?: string;
237
291
 
238
292
  /**
239
- * Template status: APPROVED, PENDING, REJECTED
293
+ * Template status: DRAFT, PENDING, APPROVED, REJECTED. A template created with
294
+ * submit_for_review: false starts as DRAFT and stays there until it is submitted.
240
295
  */
241
296
  status?: string;
242
297
 
@@ -252,37 +307,142 @@ export interface Template {
252
307
  }
253
308
 
254
309
  /**
255
- * Body section of a message template with channel-specific content
310
+ * Body section of a message template.
311
+ *
312
+ * A body picks one of two authoring strategies, and mixing them is refused
313
+ * (TemplateDefinitionValidator.HaveValidChannelConfiguration): a shared
314
+ * multiChannel body on its own, or an explicit sms + whatsapp pair, both present.
315
+ *
316
+ * multiChannel together with sms or whatsapp is rejected, and so is sms or
317
+ * whatsapp on its own — every template is expected to be deliverable on every
318
+ * channel. rcs is the one true override: it may accompany either strategy to vary
319
+ * the copy, but cannot stand alone.
256
320
  */
257
321
  export interface TemplateBody {
258
322
  /**
259
- * Content that will be used for all channels (SMS and WhatsApp) unless
260
- * channel-specific content is provided
323
+ * MMS-specific content — subject, text and attachments.
324
+ *
325
+ * Like Rcs, an override that cannot stand on its own: a template still needs a
326
+ * MultiChannel body or the Sms + Whatsapp pair to be deliverable at all. Unlike
327
+ * Rcs, it has no fallback at send time — MMS with no media is a more expensive
328
+ * SMS, so a template without this slot is deliberately not MMS-capable and never
329
+ * produces an MMS route candidate.
330
+ */
331
+ mms?: TemplateBody.Mms | null;
332
+
333
+ /**
334
+ * The shared body, used for every channel. One half of the choice described above.
261
335
  */
262
336
  multiChannel?: TemplateBodyContent | null;
263
337
 
264
338
  /**
265
- * RCS-specific content that overrides multi-channel content for RCS messages
339
+ * RCS-specific copy that overrides the chosen strategy for RCS only. The one true
340
+ * override: optional on top of either strategy, but it cannot be the only body
341
+ * present. Its length cap is the higher one described on Template.
266
342
  */
267
343
  rcs?: TemplateBodyContent | null;
268
344
 
269
345
  /**
270
- * SMS-specific content that overrides multi-channel content for SMS messages
346
+ * The SMS body. It does not override multiChannel, it replaces it.
271
347
  */
272
348
  sms?: TemplateBodyContent | null;
273
349
 
274
350
  /**
275
- * WhatsApp-specific content that overrides multi-channel content for WhatsApp
276
- * messages
351
+ * The WhatsApp body. It does not override multiChannel, it replaces it.
277
352
  */
278
353
  whatsapp?: TemplateBodyContent | null;
279
354
  }
280
355
 
356
+ export namespace TemplateBody {
357
+ /**
358
+ * MMS-specific content — subject, text and attachments.
359
+ *
360
+ * Like Rcs, an override that cannot stand on its own: a template still needs a
361
+ * MultiChannel body or the Sms + Whatsapp pair to be deliverable at all. Unlike
362
+ * Rcs, it has no fallback at send time — MMS with no media is a more expensive
363
+ * SMS, so a template without this slot is deliberately not MMS-capable and never
364
+ * produces an MMS route candidate.
365
+ */
366
+ export interface Mms extends TemplatesAPI.TemplateBodyContent {
367
+ /**
368
+ * Attachments carried by every send on this template, in order. A per-send
369
+ * media_urls on the request replaces this list rather than adding to it, so a
370
+ * template can hold a default creative and a caller can still send something
371
+ * recipient-specific.
372
+ */
373
+ media?: Array<Mms.Media> | null;
374
+
375
+ /**
376
+ * MMS subject line. Optional — most handsets render it above the body, some ignore
377
+ * it entirely. Deliberately its own field rather than riding TemplateHeader: the
378
+ * header is authored once and shared across every channel, and carries Meta's
379
+ * 60-character cap plus its no-newline, no-emoji text rules, none of which
380
+ * describe an MMS subject.
381
+ */
382
+ subject?: string | null;
383
+ }
384
+
385
+ export namespace Mms {
386
+ /**
387
+ * One attachment on an MMS template body.
388
+ */
389
+ export interface Media {
390
+ /**
391
+ * One of MmsMediaTypes. Advisory: the carrier reads the Content-Type off the
392
+ * fetched object, not this field. It exists so an authoring UI can render the
393
+ * right preview and so a reviewer can see what was intended.
394
+ */
395
+ mediaType?: string | null;
396
+
397
+ /**
398
+ * Publicly fetchable https URL. The carrier's MMSC fetches this at send time, so
399
+ * it has to stay reachable and unauthenticated for the life of the send —
400
+ * including retries and a DLQ replay — which is why a presigned URL is not a valid
401
+ * value here.
402
+ */
403
+ url?: string;
404
+ }
405
+ }
406
+ }
407
+
281
408
  export interface TemplateBodyContent {
409
+ /**
410
+ * The body copy, with variables written as {{index:variable}}.
411
+ *
412
+ * Length cap depends on which channel this body belongs to:
413
+ * TemplateContentLimits.MaxBodyLength (1024) for multiChannel, sms and whatsapp —
414
+ * Meta's BODY limit, which a multiChannel body may be delivered under — and
415
+ * TemplateContentLimits.MaxRcsBodyLength (3072) for an rcs body, which never
416
+ * reaches Meta. The maxLength advertised on this schema is the 1024 one, because
417
+ * all four channel bodies share this single schema — an rcs body between the two
418
+ * is accepted.
419
+ *
420
+ * Meta requires every variable to carry surrounding context, so a body is refused
421
+ * unless it also satisfies all of the following (enforced by
422
+ * TemplateDefinitionValidator): At least one letter before the first variable and
423
+ * after the last — trailing punctuation such as "... {{1:variable}}." does not
424
+ * count. At least (2 × variable count) + 1 words once the placeholders are
425
+ * removed. No two variables adjacent with only whitespace between them. No leading
426
+ * or trailing newline, no more than two consecutive line breaks, and no more than
427
+ * four consecutive spaces.
428
+ *
429
+ * Example: "Hello {{0:variable}}! Welcome to {{1:variable}}. We are glad to have
430
+ * you on board." — two variables, so at least five words are required, and the
431
+ * copy after the final variable contains letters.
432
+ */
282
433
  template: string;
283
434
 
435
+ /**
436
+ * The type of body content — send "text". It is dropped from the stored definition
437
+ * when null, so a body posted without it is saved with no type key at all and the
438
+ * template editor has nothing to render the block from.
439
+ */
284
440
  type?: string | null;
285
441
 
442
+ /**
443
+ * The variables referenced by the body copy, one entry per {{index:variable}}
444
+ * placeholder.
445
+ */
286
446
  variables?: Array<TemplateVariable> | null;
287
447
  }
288
448
 
@@ -301,7 +461,13 @@ export interface TemplateButton {
301
461
  type: string;
302
462
 
303
463
  /**
304
- * The unique identifier of the button (1-based index)
464
+ * The button's identifier (1-based index), unique within the template.
465
+ *
466
+ * Omitting it is only safe for a template holding a single button. The field is a
467
+ * non-nullable int, so every button that leaves it out defaults to 0, and two such
468
+ * buttons are refused by the unique-id rule ("Button IDs must be unique"). Number
469
+ * them from 1 in the order they should appear — order matters on RCS, where only
470
+ * the first four buttons render.
305
471
  */
306
472
  id?: number;
307
473
  }
@@ -317,6 +483,19 @@ export interface TemplateButtonProps {
317
483
 
318
484
  quickReplyType: string;
319
485
 
486
+ /**
487
+ * The button's label. Required for every button type, and capped at
488
+ * TemplateContentLimits.MaxButtonTextLength (25) characters.
489
+ *
490
+ * Meta accepts only static text here, so a label is refused when it contains a
491
+ * {{...}} variable placeholder, a newline, an emoji, or WhatsApp formatting markup
492
+ * (\*, \_, ~) — enforced by ApplyButtonLabelContentRules in
493
+ * TemplateButtonValidator. Meta reports all four as one error: "Buttons can't have
494
+ * any variables, newlines, emojis, or formatting characters."
495
+ *
496
+ * AUTHENTICATION OTP buttons are the exception: Meta auto-localizes their label
497
+ * from the template language, and the converter drops whatever text was sent.
498
+ */
320
499
  text: string;
321
500
 
322
501
  url: string;
@@ -345,7 +524,16 @@ export interface TemplateButtonProps {
345
524
  */
346
525
  export interface TemplateDefinition {
347
526
  /**
348
- * Body section of a message template with channel-specific content
527
+ * Body section of a message template.
528
+ *
529
+ * A body picks one of two authoring strategies, and mixing them is refused
530
+ * (TemplateDefinitionValidator.HaveValidChannelConfiguration): a shared
531
+ * multiChannel body on its own, or an explicit sms + whatsapp pair, both present.
532
+ *
533
+ * multiChannel together with sms or whatsapp is rejected, and so is sms or
534
+ * whatsapp on its own — every template is expected to be deliverable on every
535
+ * channel. rcs is the one true override: it may accompany either strategy to vary
536
+ * the copy, but cannot stand alone.
349
537
  */
350
538
  body: TemplateBody;
351
539
 
@@ -406,7 +594,52 @@ export interface TemplateHeader {
406
594
  template: string;
407
595
 
408
596
  /**
409
- * The type of header (e.g., "text", "image", "video", "document")
597
+ * Request-only. The s.dm URL of the asset Meta's reviewers see —
598
+ * https://s.dm/s/{ID}, eight uppercase characters, uploaded to s.dm out of band.
599
+ * NormalizeRichHeader folds it into the synthesized media variable's Props.Sample
600
+ * and clears it, so it never persists and a stored definition is indistinguishable
601
+ * from an imported one.
602
+ *
603
+ * Stricter than the send path on purpose:
604
+ * TemplateUtils.ValidateMediaVariableValues accepts any absolute https URL for the
605
+ * per-send asset, because that one is the customer's and may live behind a signed
606
+ * CDN link. This one is the review sample, has to outlive every resubmission, and
607
+ * so must be ours. Do not "fix" one to match the other.
608
+ */
609
+ example_url?: string | null;
610
+
611
+ /**
612
+ * The map pin a location header drops. Meta wants none of this at creation — the
613
+ * component is just {"type":"header","format":"location"} — so these values exist
614
+ * for Sent: a preview, and the default a StaticResource header falls back to at
615
+ * send.
616
+ */
617
+ location?: TemplateHeader.Location | null;
618
+
619
+ /**
620
+ * Whether the asset registered at creation is reused when a caller omits the
621
+ * header's variable at send time. Default false — the caller must supply it per
622
+ * message, which is the behaviour every existing template has. Written only when
623
+ * true, so a default-valued header serializes byte-identically to one imported
624
+ * from Meta.
625
+ *
626
+ * Stored and validated but not yet honoured at send: that lands with the Resumable
627
+ * Upload work, alongside the code that lets such a template be approved in the
628
+ * first place.
629
+ */
630
+ static_resource?: boolean;
631
+
632
+ /**
633
+ * The kind of header. One of:
634
+ *
635
+ * text — up to 60 characters, at most one variable. image — png, jpg or jpeg.
636
+ * Needs ExampleUrl. video — mp4. Needs ExampleUrl. gif — mp4, max 3.5MB. WhatsApp
637
+ * renders larger files as an ordinary video. Needs ExampleUrl. document — pdf or
638
+ * docx; only the first page is shown as a thumbnail, so pdf is the practical
639
+ * choice. Needs ExampleUrl. location — a map pin, supplied through Location.
640
+ *
641
+ * Kept lowercase because MetaToTemplateConverter writes Meta's format through
642
+ * ToLowerInvariant() into this field on import, and the two are compared directly.
410
643
  */
411
644
  type?: string | null;
412
645
 
@@ -416,13 +649,48 @@ export interface TemplateHeader {
416
649
  variables?: Array<TemplateVariable> | null;
417
650
  }
418
651
 
652
+ export namespace TemplateHeader {
653
+ /**
654
+ * The map pin a location header drops. Meta wants none of this at creation — the
655
+ * component is just {"type":"header","format":"location"} — so these values exist
656
+ * for Sent: a preview, and the default a StaticResource header falls back to at
657
+ * send.
658
+ */
659
+ export interface Location {
660
+ address: string;
661
+
662
+ latitude: string;
663
+
664
+ longitude: string;
665
+
666
+ name: string;
667
+ }
668
+ }
669
+
419
670
  export interface TemplateVariable {
671
+ /**
672
+ * The variable's name, and the key callers use for it in a send request's
673
+ * parameters object. Must start with a letter and hold only letters, digits and
674
+ * underscores.
675
+ */
420
676
  name: string;
421
677
 
422
678
  props: TemplateVariable.Props;
423
679
 
680
+ /**
681
+ * One of variable, link or media. Decides which Props fields are required.
682
+ */
424
683
  type: string;
425
684
 
685
+ /**
686
+ * The variable's index, and the number its {{index:variable}} placeholder refers
687
+ * to.
688
+ *
689
+ * Omitting it is only safe for a section holding a single variable. The field is a
690
+ * non-nullable int, so every variable that leaves it out defaults to 0, and a
691
+ * section with two such variables is refused by the unique-id rule ("variables
692
+ * must have unique IDs"). Number them from 0 in the order they appear.
693
+ */
426
694
  id?: number;
427
695
  }
428
696
 
@@ -430,6 +698,13 @@ export namespace TemplateVariable {
430
698
  export interface Props {
431
699
  mediaType: string;
432
700
 
701
+ /**
702
+ * Example value substituted into the template when previewing it and when
703
+ * submitting it to Meta for review. Free text by nature, so the converter accepts
704
+ * a JSON number or boolean here and normalizes it — see
705
+ * JsonScalarToStringConverter for why — and guarantees it is always serialized
706
+ * back out as a JSON string.
707
+ */
433
708
  sample: string;
434
709
 
435
710
  url: string;