@sentdm/sentdm 0.39.0 → 0.41.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 (55) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/client.d.mts +2 -0
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +2 -0
  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 +126 -3
  12. package/resources/conversations.d.mts.map +1 -1
  13. package/resources/conversations.d.ts +126 -3
  14. package/resources/conversations.d.ts.map +1 -1
  15. package/resources/me.d.mts +21 -0
  16. package/resources/me.d.mts.map +1 -1
  17. package/resources/me.d.ts +21 -0
  18. package/resources/me.d.ts.map +1 -1
  19. package/resources/me.js +5 -0
  20. package/resources/me.js.map +1 -1
  21. package/resources/me.mjs +5 -0
  22. package/resources/me.mjs.map +1 -1
  23. package/resources/messages.d.mts +239 -14
  24. package/resources/messages.d.mts.map +1 -1
  25. package/resources/messages.d.ts +239 -14
  26. package/resources/messages.d.ts.map +1 -1
  27. package/resources/messages.js +21 -3
  28. package/resources/messages.js.map +1 -1
  29. package/resources/messages.mjs +21 -3
  30. package/resources/messages.mjs.map +1 -1
  31. package/resources/templates.d.mts +130 -1
  32. package/resources/templates.d.mts.map +1 -1
  33. package/resources/templates.d.ts +130 -1
  34. package/resources/templates.d.ts.map +1 -1
  35. package/resources/templates.js.map +1 -1
  36. package/resources/templates.mjs.map +1 -1
  37. package/resources/webhooks.d.mts +616 -30
  38. package/resources/webhooks.d.mts.map +1 -1
  39. package/resources/webhooks.d.ts +616 -30
  40. package/resources/webhooks.d.ts.map +1 -1
  41. package/resources/webhooks.js +3 -1
  42. package/resources/webhooks.js.map +1 -1
  43. package/resources/webhooks.mjs +3 -1
  44. package/resources/webhooks.mjs.map +1 -1
  45. package/src/client.ts +2 -0
  46. package/src/resources/conversations.ts +140 -3
  47. package/src/resources/me.ts +22 -0
  48. package/src/resources/messages.ts +259 -14
  49. package/src/resources/templates.ts +143 -1
  50. package/src/resources/webhooks.ts +678 -31
  51. package/src/version.ts +1 -1
  52. package/version.d.mts +1 -1
  53. package/version.d.ts +1 -1
  54. package/version.js +1 -1
  55. package/version.mjs +1 -1
@@ -13,11 +13,15 @@ import { path } from '../internal/utils/path';
13
13
  * One endpoint sends on any channel: pass `channel: "sent"` and we pick between SMS, WhatsApp and RCS per recipient using your routing rules, or name a channel to pin it. A send is accepted asynchronously — `POST /v3/messages` returns an id, and delivery is reported through `GET /v3/messages/{id}`, its activities, or a webhook.
14
14
  *
15
15
  * **A message needs a sender.** What you can send, where, and at what cost is decided by the markets under **Channels** — so a recipient in a country you hold no sender for is refused here rather than queued.
16
+ *
17
+ * **A message can be resent on its id.** `POST /v3/messages/{id}/resend` puts a finished message — typically one BLOCKED for insufficient balance — back through the send pipeline. It is a new attempt, not a free retry: every policy runs again, the message is billed again, and its status webhooks fire again. A FILTERED message is never resendable.
16
18
  */
17
19
  export class Messages extends APIResource {
18
20
  /**
19
21
  * Retrieves the activity log for a specific message. Activities track the message
20
- * lifecycle including acceptance, processing, sending, delivery, and any errors.
22
+ * lifecycle including acceptance, processing, sending, delivery, and any errors. A
23
+ * SCHEDULED entry carries scheduled_at, the release instant in UTC as it stood at
24
+ * that moment. Other entries have no scheduled_at key.
21
25
  *
22
26
  * @example
23
27
  * ```ts
@@ -43,7 +47,11 @@ export class Messages extends APIResource {
43
47
 
44
48
  /**
45
49
  * Retrieves the current status and details of a message by ID. Includes delivery
46
- * status, timestamps, and error information if applicable.
50
+ * status, timestamps, and error information if applicable. A message that is or
51
+ * was held for a later time (a send you scheduled with scheduled_at, or a
52
+ * quiet-hours hold) is returned as a ScheduledMessageResponse: the same fields
53
+ * plus scheduled_at, the release instant in UTC. A message sent immediately has no
54
+ * scheduled_at key.
47
55
  *
48
56
  * @example
49
57
  * ```ts
@@ -77,7 +85,17 @@ export class Messages extends APIResource {
77
85
  * insufficient balance, a template not approved for sending, or free-form content
78
86
  * with no open conversation with the contact. The send is accepted with 202 and
79
87
  * the affected messages are reported as BLOCKED on GET /messages/{id} and the
80
- * message.blocked webhook.
88
+ * message.blocked webhook. To send later, set scheduled_at (ISO-8601 with an
89
+ * explicit UTC offset; a value without one is rejected) between 1 minute and 30
90
+ * days ahead: the response is a ScheduledSendMessageResponse (the same fields plus
91
+ * scheduled_at; status is still QUEUED), each message then moves to SCHEDULED, is
92
+ * held and released at that time (within a few minutes), and a message.scheduled
93
+ * webhook fires once it is held. Balance and template approval are evaluated at
94
+ * release, not at acceptance. Quiet hours are not checked when the request is
95
+ * accepted: if the time falls inside a legally protected quiet-hours window for a
96
+ * recipient, that message is moved to the next allowed time at release and a
97
+ * second message.scheduled webhook reports the new scheduled_at. An account may
98
+ * hold at most 1,000,000 scheduled messages at once (429 LIMIT_001).
81
99
  *
82
100
  * @example
83
101
  * ```ts
@@ -148,7 +166,11 @@ export namespace MessageRetrieveActivitiesResponse {
148
166
 
149
167
  export namespace Data {
150
168
  /**
151
- * A single message activity event for v3 API
169
+ * A single message activity event for v3 API.
170
+ *
171
+ * The activity list mixes statuses, so unlike a message it is one shape rather
172
+ * than two: a SCHEDULED entry carries scheduled_at, and every other entry has no
173
+ * such key.
152
174
  */
153
175
  export interface Activity {
154
176
  /**
@@ -176,8 +198,31 @@ export namespace MessageRetrieveActivitiesResponse {
176
198
  price?: string | null;
177
199
 
178
200
  /**
179
- * Activity status. Outbound: QUEUED, PROCESSED, ROUTED, SENT, DELIVERED, READ,
180
- * FAILED. Inbound (from contact): RECEIVED (terminal).
201
+ * A human-readable sentence for reason_code, for example "The recipient is not
202
+ * registered on this channel" Omitted whenever reason_code is.
203
+ */
204
+ reason?: string | null;
205
+
206
+ /**
207
+ * Why the message reached this status, as a stable platform code such as
208
+ * DELIVERY_007 or BUSINESS_003. Present on FAILED, FILTERED and BLOCKED
209
+ * activities; omitted on every status that needs no explanation. Switch on this
210
+ * rather than on reason: the code is stable, the wording may be improved. Same
211
+ * wire name and vocabulary as on the message and the webhook.
212
+ */
213
+ reason_code?: string | null;
214
+
215
+ /**
216
+ * SCHEDULED activities only: when the held message will be released for delivery,
217
+ * in UTC. Same wire name as on the send response, the message and the webhook.
218
+ * Omitted on every other activity. A message that quiet hours moved at release has
219
+ * two SCHEDULED entries, each carrying the instant as it stood at that moment.
220
+ */
221
+ scheduled_at?: string | null;
222
+
223
+ /**
224
+ * Activity status. Outbound: QUEUED, PROCESSED, ROUTED, SCHEDULED, SENT,
225
+ * DELIVERED, READ, FAILED. Inbound (from contact): RECEIVED (terminal).
181
226
  */
182
227
  status?: string;
183
228
 
@@ -194,7 +239,12 @@ export namespace MessageRetrieveActivitiesResponse {
194
239
  */
195
240
  export interface MessageRetrieveStatusResponse {
196
241
  /**
197
- * Message response for v3 API — same shape as v2 with snake_case JSON conventions
242
+ * Message response for v3 API — same shape as v2 with snake_case JSON conventions.
243
+ *
244
+ * The shape of a message that was sent immediately: it never has a scheduled_at
245
+ * key. A message that is or was held for a later instant is a
246
+ * ScheduledMessageResponse, and the endpoint decides which of the two to answer
247
+ * with. From always returns this type.
198
248
  */
199
249
  data?: MessageRetrieveStatusResponse.Data | null;
200
250
 
@@ -216,7 +266,12 @@ export interface MessageRetrieveStatusResponse {
216
266
 
217
267
  export namespace MessageRetrieveStatusResponse {
218
268
  /**
219
- * Message response for v3 API — same shape as v2 with snake_case JSON conventions
269
+ * Message response for v3 API — same shape as v2 with snake_case JSON conventions.
270
+ *
271
+ * The shape of a message that was sent immediately: it never has a scheduled_at
272
+ * key. A message that is or was held for a later instant is a
273
+ * ScheduledMessageResponse, and the endpoint decides which of the two to answer
274
+ * with. From always returns this type.
220
275
  */
221
276
  export interface Data {
222
277
  id?: string;
@@ -237,7 +292,14 @@ export namespace MessageRetrieveStatusResponse {
237
292
 
238
293
  /**
239
294
  * Structured message body format for database storage. Preserves channel-specific
240
- * components (header, body, footer, buttons).
295
+ * components (header, header media, body, footer, buttons, MMS subject and media).
296
+ *
297
+ * Persisted as the messageBody jsonb column on Messages. Every write path goes
298
+ * through MessageUtils.MessageBodyJsonOptions, which writes nulls, so the envelope
299
+ * shape is stable regardless of channel or status. Anything that rebuilds this
300
+ * object field by field — the four IMessageBodyStrategy implementations and
301
+ * MessageUtils.BuildSegmentBody — has to carry every member, or that member is
302
+ * silently dropped on whichever path forgot it.
241
303
  */
242
304
  message_body?: Data.MessageBody | null;
243
305
 
@@ -247,6 +309,22 @@ export namespace MessageRetrieveStatusResponse {
247
309
 
248
310
  price?: number | null;
249
311
 
312
+ /**
313
+ * A human-readable sentence for reason_code, for example "Insufficient balance".
314
+ * Omitted whenever reason_code is.
315
+ */
316
+ reason?: string | null;
317
+
318
+ /**
319
+ * Why the message is at its current status, as a stable platform code such as
320
+ * DELIVERY_007, BUSINESS_003 or DELIVERY_003. Present when the current status is
321
+ * FAILED, FILTERED or BLOCKED and the lifecycle was loaded; omitted otherwise.
322
+ * Switch on this rather than on reason: the code is stable, the wording may be
323
+ * improved. It is the platform's classification of the outcome, never a carrier or
324
+ * vendor code.
325
+ */
326
+ reason_code?: string | null;
327
+
250
328
  region_code?: string;
251
329
 
252
330
  status?: string;
@@ -268,11 +346,31 @@ export namespace MessageRetrieveStatusResponse {
268
346
  timestamp: string;
269
347
 
270
348
  description?: string | null;
349
+
350
+ /**
351
+ * A human-readable sentence for reason_code. Omitted whenever reason_code is.
352
+ */
353
+ reason?: string | null;
354
+
355
+ /**
356
+ * Why the message reached this status, as a stable platform code such as
357
+ * DELIVERY_007. Present on FAILED, FILTERED and BLOCKED events; omitted on every
358
+ * status that needs no explanation. Same wire name and vocabulary as on the
359
+ * activities list and the webhook.
360
+ */
361
+ reason_code?: string | null;
271
362
  }
272
363
 
273
364
  /**
274
365
  * Structured message body format for database storage. Preserves channel-specific
275
- * components (header, body, footer, buttons).
366
+ * components (header, header media, body, footer, buttons, MMS subject and media).
367
+ *
368
+ * Persisted as the messageBody jsonb column on Messages. Every write path goes
369
+ * through MessageUtils.MessageBodyJsonOptions, which writes nulls, so the envelope
370
+ * shape is stable regardless of channel or status. Anything that rebuilds this
371
+ * object field by field — the four IMessageBodyStrategy implementations and
372
+ * MessageUtils.BuildSegmentBody — has to carry every member, or that member is
373
+ * silently dropped on whichever path forgot it.
276
374
  */
277
375
  export interface MessageBody {
278
376
  buttons?: Array<MessageBody.Button> | null;
@@ -282,6 +380,27 @@ export namespace MessageRetrieveStatusResponse {
282
380
  footer?: string | null;
283
381
 
284
382
  header?: string | null;
383
+
384
+ /**
385
+ * The media asset that rode a message's header, recorded as sent.
386
+ */
387
+ headerMedia?: MessageBody.HeaderMedia | null;
388
+
389
+ /**
390
+ * MMS attachments, as the publicly fetchable URLs handed to the carrier. Null on
391
+ * every other channel.
392
+ *
393
+ * Persisted rather than derived because a resend and a curfew release rebuild the
394
+ * send from the stored row — MessageReplayCommandBuilder reads templateId and
395
+ * templateVariables and nothing else — so media that lives only on the original
396
+ * request would silently turn a replayed MMS into a text message.
397
+ */
398
+ media?: Array<MessageBody.Media> | null;
399
+
400
+ /**
401
+ * MMS subject line. Null on every other channel.
402
+ */
403
+ subject?: string | null;
285
404
  }
286
405
 
287
406
  export namespace MessageBody {
@@ -294,6 +413,74 @@ export namespace MessageRetrieveStatusResponse {
294
413
 
295
414
  value?: string;
296
415
  }
416
+
417
+ /**
418
+ * The media asset that rode a message's header, recorded as sent.
419
+ */
420
+ export interface HeaderMedia {
421
+ /**
422
+ * "image", "video" or "document" — taken from the header's media variable.
423
+ */
424
+ type?: string;
425
+
426
+ /**
427
+ * The https URL the caller supplied for this send. Never the template's stored
428
+ * props.sample, which is Meta's expiring header_handle rather than what was
429
+ * delivered.
430
+ */
431
+ url?: string;
432
+ }
433
+
434
+ /**
435
+ * One attachment on a message, in either direction — and in both, a URL somebody
436
+ * else hosts.
437
+ *
438
+ * Outbound: the customer supplied a public URL and we handed it to the carrier.
439
+ * Inbound: the carrier hosts the file and we record where. sent.dm never holds the
440
+ * bytes, so there is no key, no expiry bookkeeping and nothing minted per read —
441
+ * what is stored is what is served.
442
+ *
443
+ * An inbound link expires on the carrier's own schedule and is unauthenticated.
444
+ * That is the customer's to manage, and it is documented where they will see it
445
+ * rather than only here — a recipient who needs an attachment to outlive that
446
+ * window copies it on receipt.
447
+ *
448
+ * Storing a presigned URL is the specific mistake this shape still avoids:
449
+ * M260826130000 and M260826140000 exist because RCS assets were stored as signed
450
+ * URLs and went stale. Nothing here is signed.
451
+ */
452
+ export interface Media {
453
+ /**
454
+ * One of MmsMediaTypes when the content type is known. Advisory — a reader should
455
+ * trust the fetched object's own Content-Type.
456
+ */
457
+ mediaType?: string | null;
458
+
459
+ /**
460
+ * Content type as the provider declared it. Null when it declared none.
461
+ */
462
+ mimeType?: string | null;
463
+
464
+ /**
465
+ * Size as the provider declared it. Never measured here — nothing downloads the
466
+ * file.
467
+ */
468
+ sizeBytes?: number | null;
469
+
470
+ /**
471
+ * Inbound only: the SHA-256 the provider declared alongside the attachment, when
472
+ * it declared one. Relayed to the customer so they can verify what they fetch
473
+ * matches what the carrier said it sent. It is the only integrity signal available
474
+ * on an attachment nobody here has read.
475
+ */
476
+ sourceHashSha256?: string | null;
477
+
478
+ /**
479
+ * Where the file lives. Outbound: the URL the customer gave us and the carrier
480
+ * fetched. Inbound: the URL the carrier hosts it at, relayed unchanged.
481
+ */
482
+ url?: string | null;
483
+ }
297
484
  }
298
485
  }
299
486
  }
@@ -312,7 +499,9 @@ export interface MessageSendResponse {
312
499
  * its result; this is what a caller sees, and the mapping between them is a
313
500
  * decision the endpoint makes.
314
501
  *
315
- * The wire is unchanged by the move: same names, same values.
502
+ * The shape of an immediate send: it never has a scheduled_at key. A send that
503
+ * carried scheduled_at is a ScheduledSendMessageResponse, and the endpoint decides
504
+ * which of the two to answer with. From always returns this type.
316
505
  */
317
506
  data?: MessageSendResponse.Data | null;
318
507
 
@@ -343,13 +532,17 @@ export namespace MessageSendResponse {
343
532
  * its result; this is what a caller sees, and the mapping between them is a
344
533
  * decision the endpoint makes.
345
534
  *
346
- * The wire is unchanged by the move: same names, same values.
535
+ * The shape of an immediate send: it never has a scheduled_at key. A send that
536
+ * carried scheduled_at is a ScheduledSendMessageResponse, and the endpoint decides
537
+ * which of the two to answer with. From always returns this type.
347
538
  */
348
539
  export interface Data {
349
540
  recipients?: Array<Data.Recipient>;
350
541
 
351
542
  /**
352
- * Overall status — QUEUED once the batch is accepted for delivery.
543
+ * QUEUED: the batch is accepted. A request that carried scheduled_at is QUEUED
544
+ * here too; each message moves to SCHEDULED once it is held, as GET
545
+ * /v3/messages/{id} and the message.scheduled webhook report.
353
546
  */
354
547
  status?: string;
355
548
 
@@ -411,12 +604,51 @@ export interface MessageSendParams {
411
604
  */
412
605
  channel?: Array<string> | null;
413
606
 
607
+ /**
608
+ * Body param: Attachments for this send, as publicly fetchable https URLs. Used by
609
+ * the MMS channel and ignored by every other one.
610
+ *
611
+ * Supplying these replaces the media on the template's mms body rather than adding
612
+ * to it, so a template can hold a default creative while a caller still sends
613
+ * something recipient-specific.
614
+ *
615
+ * Their presence is also what makes a message eligible for MMS on an auto-detect
616
+ * send: a message with nothing attached is delivered as SMS, because an MMS with
617
+ * no media is a more expensive text message.
618
+ *
619
+ * The recipient's carrier fetches each URL after the send is accepted, so it must
620
+ * stay publicly reachable — a link that expires, or one behind auth, arrives as a
621
+ * failed message.
622
+ */
623
+ media_urls?: Array<string> | null;
624
+
414
625
  /**
415
626
  * Body param: Sandbox flag - when true, the operation is simulated without side
416
627
  * effects Useful for testing integrations without actual execution
417
628
  */
418
629
  sandbox?: boolean;
419
630
 
631
+ /**
632
+ * Body param: Optional future send time as an ISO-8601 timestamp with an explicit
633
+ * UTC offset, e.g. 2026-10-01T09:00:00+02:00 or 2026-10-01T07:00:00Z. A value
634
+ * without an offset is rejected (400) rather than read in the server's zone. The
635
+ * offset only fixes the instant: it is stored and echoed in UTC as scheduled_at.
636
+ * Omit to send now. Must be at least one minute ahead and at most 30 days ahead.
637
+ * Accepted messages report SCHEDULED and are released for delivery at this time.
638
+ * Quiet hours, balance and template approval are evaluated at release, not at
639
+ * acceptance: a message whose time falls inside a recipient's protected
640
+ * quiet-hours window is moved to the next allowed time and a second
641
+ * message.scheduled webhook reports the new scheduled_at.
642
+ */
643
+ scheduled_at?: string | null;
644
+
645
+ /**
646
+ * Body param: Subject line for this send, overriding the template's. MMS only;
647
+ * ignored on every other channel. Most handsets render it above the body, some
648
+ * ignore it entirely.
649
+ */
650
+ subject?: string | null;
651
+
420
652
  /**
421
653
  * Body param: SDK-style template reference: resolve by ID or by name, with
422
654
  * optional parameters.
@@ -467,7 +699,20 @@ export namespace MessageSendParams {
467
699
  name?: string | null;
468
700
 
469
701
  /**
470
- * Template variable parameters for personalization
702
+ * Template variable parameters for personalization, keyed by variable name.
703
+ *
704
+ * Every variable the template declares is required; GET /v3/templates/{id} lists
705
+ * them. Supplying a key the template does not declare is ignored.
706
+ *
707
+ * Media headers. A template whose header is an image (designed in WhatsApp Manager
708
+ * and imported into Sent) declares a reserved header_image key. Its value is a
709
+ * publicly reachable https URL that Meta fetches at send time — Sent does not host
710
+ * the asset, and the sample approved with the template is not reused. The key is
711
+ * derived from the header's media type, so header_video and header_document follow
712
+ * the same shape when those formats ship.
713
+ *
714
+ * "parameters": { "header_image": "https://cdn.example.com/banner.jpg", "name":
715
+ * "John Doe" }
471
716
  */
472
717
  parameters?: { [key: string]: string } | null;
473
718
  }
@@ -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';
@@ -318,6 +319,17 @@ export interface Template {
318
319
  * the copy, but cannot stand alone.
319
320
  */
320
321
  export interface TemplateBody {
322
+ /**
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
+
321
333
  /**
322
334
  * The shared body, used for every channel. One half of the choice described above.
323
335
  */
@@ -341,6 +353,58 @@ export interface TemplateBody {
341
353
  whatsapp?: TemplateBodyContent | null;
342
354
  }
343
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
+
344
408
  export interface TemplateBodyContent {
345
409
  /**
346
410
  * The body copy, with variables written as {{index:variable}}.
@@ -530,7 +594,52 @@ export interface TemplateHeader {
530
594
  template: string;
531
595
 
532
596
  /**
533
- * 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.
534
643
  */
535
644
  type?: string | null;
536
645
 
@@ -540,6 +649,24 @@ export interface TemplateHeader {
540
649
  variables?: Array<TemplateVariable> | null;
541
650
  }
542
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
+
543
670
  export interface TemplateVariable {
544
671
  /**
545
672
  * The variable's name, and the key callers use for it in a send request's
@@ -571,6 +698,13 @@ export namespace TemplateVariable {
571
698
  export interface Props {
572
699
  mediaType: string;
573
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
+ */
574
708
  sample: string;
575
709
 
576
710
  url: string;
@@ -586,6 +720,14 @@ export namespace TemplateVariable {
586
720
  }
587
721
 
588
722
  export interface TemplateCreateParams {
723
+ /**
724
+ * Body param: Create this template automatically on every sender profile of the
725
+ * organization, now and in future (default: false). Accepted only from an
726
+ * organization that has been enabled for it, and only at creation — it cannot be
727
+ * changed afterwards.
728
+ */
729
+ auto_create_for_sp?: boolean;
730
+
589
731
  /**
590
732
  * Body param: Template category: MARKETING, UTILITY, AUTHENTICATION (optional,
591
733
  * auto-detected if not provided)