sentdm 0.33.0 → 0.34.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +1 -1
  4. data/lib/sentdm/models/channel_event_payload.rb +16 -6
  5. data/lib/sentdm/models/conversation_messages_list.rb +100 -13
  6. data/lib/sentdm/models/inbound_message_event_payload.rb +70 -3
  7. data/lib/sentdm/models/me_retrieve_response.rb +21 -1
  8. data/lib/sentdm/models/message_event_payload.rb +23 -1
  9. data/lib/sentdm/models/message_retrieve_activities_response.rb +23 -1
  10. data/lib/sentdm/models/message_retrieve_status_response.rb +100 -13
  11. data/lib/sentdm/models/template_create_params.rb +11 -1
  12. data/lib/sentdm/models/webhook_create_params.rb +31 -1
  13. data/lib/sentdm/models/webhook_list_events_response.rb +176 -3
  14. data/lib/sentdm/models/webhook_update_params.rb +31 -1
  15. data/lib/sentdm/resources/me.rb +5 -0
  16. data/lib/sentdm/resources/templates.rb +3 -1
  17. data/lib/sentdm/resources/webhooks.rb +9 -3
  18. data/lib/sentdm/version.rb +1 -1
  19. data/rbi/sentdm/models/channel_event_payload.rbi +20 -8
  20. data/rbi/sentdm/models/conversation_messages_list.rbi +122 -19
  21. data/rbi/sentdm/models/inbound_message_event_payload.rbi +110 -2
  22. data/rbi/sentdm/models/me_retrieve_response.rbi +35 -0
  23. data/rbi/sentdm/models/message_event_payload.rbi +28 -0
  24. data/rbi/sentdm/models/message_retrieve_activities_response.rbi +26 -0
  25. data/rbi/sentdm/models/message_retrieve_status_response.rbi +122 -19
  26. data/rbi/sentdm/models/template_create_params.rbi +15 -0
  27. data/rbi/sentdm/models/webhook_create_params.rbi +61 -0
  28. data/rbi/sentdm/models/webhook_list_events_response.rbi +272 -3
  29. data/rbi/sentdm/models/webhook_update_params.rbi +61 -0
  30. data/rbi/sentdm/resources/me.rbi +5 -0
  31. data/rbi/sentdm/resources/templates.rbi +6 -0
  32. data/rbi/sentdm/resources/webhooks.rbi +15 -1
  33. data/sig/sentdm/models/channel_event_payload.rbs +5 -0
  34. data/sig/sentdm/models/conversation_messages_list.rbs +56 -8
  35. data/sig/sentdm/models/inbound_message_event_payload.rbs +37 -0
  36. data/sig/sentdm/models/me_retrieve_response.rbs +7 -0
  37. data/sig/sentdm/models/message_event_payload.rbs +10 -0
  38. data/sig/sentdm/models/message_retrieve_activities_response.rbs +10 -0
  39. data/sig/sentdm/models/message_retrieve_status_response.rbs +56 -8
  40. data/sig/sentdm/models/template_create_params.rbs +7 -0
  41. data/sig/sentdm/models/webhook_create_params.rbs +29 -0
  42. data/sig/sentdm/models/webhook_list_events_response.rbs +107 -0
  43. data/sig/sentdm/models/webhook_update_params.rbs +29 -0
  44. data/sig/sentdm/resources/templates.rbs +1 -0
  45. data/sig/sentdm/resources/webhooks.rbs +2 -0
  46. metadata +1 -1
@@ -152,6 +152,20 @@ module Sentdm
152
152
  sig { returns(T.nilable(Float)) }
153
153
  attr_accessor :price
154
154
 
155
+ # A human-readable sentence for reason_code, for example "Insufficient balance".
156
+ # Omitted whenever reason_code is.
157
+ sig { returns(T.nilable(String)) }
158
+ attr_accessor :reason
159
+
160
+ # Why the message is at its current status, as a stable platform code such as
161
+ # DELIVERY_007, BUSINESS_003 or DELIVERY_003. Present when the current status is
162
+ # FAILED, FILTERED or BLOCKED and the lifecycle was loaded; omitted otherwise.
163
+ # Switch on this rather than on reason: the code is stable, the wording may be
164
+ # improved. It is the platform's classification of the outcome, never a carrier or
165
+ # vendor code.
166
+ sig { returns(T.nilable(String)) }
167
+ attr_accessor :reason_code
168
+
155
169
  sig { returns(T.nilable(String)) }
156
170
  attr_reader :region_code
157
171
 
@@ -201,6 +215,8 @@ module Sentdm
201
215
  phone: String,
202
216
  phone_international: String,
203
217
  price: T.nilable(Float),
218
+ reason: T.nilable(String),
219
+ reason_code: T.nilable(String),
204
220
  region_code: String,
205
221
  status: String,
206
222
  template_category: T.nilable(String),
@@ -230,6 +246,16 @@ module Sentdm
230
246
  phone: nil,
231
247
  phone_international: nil,
232
248
  price: nil,
249
+ # A human-readable sentence for reason_code, for example "Insufficient balance".
250
+ # Omitted whenever reason_code is.
251
+ reason: nil,
252
+ # Why the message is at its current status, as a stable platform code such as
253
+ # DELIVERY_007, BUSINESS_003 or DELIVERY_003. Present when the current status is
254
+ # FAILED, FILTERED or BLOCKED and the lifecycle was loaded; omitted otherwise.
255
+ # Switch on this rather than on reason: the code is stable, the wording may be
256
+ # improved. It is the platform's classification of the outcome, never a carrier or
257
+ # vendor code.
258
+ reason_code: nil,
233
259
  region_code: nil,
234
260
  status: nil,
235
261
  template_category: nil,
@@ -259,6 +285,8 @@ module Sentdm
259
285
  phone: String,
260
286
  phone_international: String,
261
287
  price: T.nilable(Float),
288
+ reason: T.nilable(String),
289
+ reason_code: T.nilable(String),
262
290
  region_code: String,
263
291
  status: String,
264
292
  template_category: T.nilable(String),
@@ -288,15 +316,39 @@ module Sentdm
288
316
  sig { returns(T.nilable(String)) }
289
317
  attr_accessor :description
290
318
 
319
+ # A human-readable sentence for reason_code. Omitted whenever reason_code is.
320
+ sig { returns(T.nilable(String)) }
321
+ attr_accessor :reason
322
+
323
+ # Why the message reached this status, as a stable platform code such as
324
+ # DELIVERY_007. Present on FAILED, FILTERED and BLOCKED events; omitted on every
325
+ # status that needs no explanation. Same wire name and vocabulary as on the
326
+ # activities list and the webhook.
327
+ sig { returns(T.nilable(String)) }
328
+ attr_accessor :reason_code
329
+
291
330
  # Represents a status change event in a message's lifecycle (v3)
292
331
  sig do
293
332
  params(
294
333
  status: String,
295
334
  timestamp: Time,
296
- description: T.nilable(String)
335
+ description: T.nilable(String),
336
+ reason: T.nilable(String),
337
+ reason_code: T.nilable(String)
297
338
  ).returns(T.attached_class)
298
339
  end
299
- def self.new(status:, timestamp:, description: nil)
340
+ def self.new(
341
+ status:,
342
+ timestamp:,
343
+ description: nil,
344
+ # A human-readable sentence for reason_code. Omitted whenever reason_code is.
345
+ reason: nil,
346
+ # Why the message reached this status, as a stable platform code such as
347
+ # DELIVERY_007. Present on FAILED, FILTERED and BLOCKED events; omitted on every
348
+ # status that needs no explanation. Same wire name and vocabulary as on the
349
+ # activities list and the webhook.
350
+ reason_code: nil
351
+ )
300
352
  end
301
353
 
302
354
  sig do
@@ -304,7 +356,9 @@ module Sentdm
304
356
  {
305
357
  status: String,
306
358
  timestamp: Time,
307
- description: T.nilable(String)
359
+ description: T.nilable(String),
360
+ reason: T.nilable(String),
361
+ reason_code: T.nilable(String)
308
362
  }
309
363
  )
310
364
  end
@@ -571,38 +625,87 @@ module Sentdm
571
625
  )
572
626
  end
573
627
 
574
- # One of Constants.MmsMediaTypes when known. Advisory — the carrier reads the
575
- # fetched object's Content-Type, not this.
628
+ # One of MmsMediaTypes when the content type is known. Advisory — a reader should
629
+ # trust the fetched object's own Content-Type.
576
630
  sig { returns(T.nilable(String)) }
577
631
  attr_accessor :media_type
578
632
 
633
+ # Content type as the provider declared it. Null when it declared none.
579
634
  sig { returns(T.nilable(String)) }
580
- attr_reader :url
635
+ attr_accessor :mime_type
581
636
 
582
- sig { params(url: String).void }
583
- attr_writer :url
637
+ # Size as the provider declared it. Never measured here — nothing downloads the
638
+ # file.
639
+ sig { returns(T.nilable(Integer)) }
640
+ attr_accessor :size_bytes
584
641
 
585
- # One attachment on a message: a customer-supplied public URL handed to the
586
- # carrier as-is.
642
+ # Inbound only: the SHA-256 the provider declared alongside the attachment, when
643
+ # it declared one. Relayed to the customer so they can verify what they fetch
644
+ # matches what the carrier said it sent. It is the only integrity signal available
645
+ # on an attachment nobody here has read.
646
+ sig { returns(T.nilable(String)) }
647
+ attr_accessor :source_hash_sha256
648
+
649
+ # Where the file lives. Outbound: the URL the customer gave us and the carrier
650
+ # fetched. Inbound: the URL the carrier hosts it at, relayed unchanged.
651
+ sig { returns(T.nilable(String)) }
652
+ attr_accessor :url
653
+
654
+ # One attachment on a message, in either direction — and in both, a URL somebody
655
+ # else hosts.
587
656
  #
588
- # A URL and nothing else. sent.dm never takes custody of MMS media — the customer hosts it and we
589
- # pass the link through at send time — so there is no storage key, size or expiry to record. If we ever
590
- # do host attachments, that belongs with the change that introduces the hosting, not here.
657
+ # Outbound: the customer supplied a public URL and we handed it to the carrier.
658
+ # Inbound: the carrier hosts the file and we record where. sent.dm never holds the
659
+ # bytes, so there is no key, no expiry bookkeeping and nothing minted per read —
660
+ # what is stored is what is served.
661
+ #
662
+ # An inbound link expires on the carrier's own schedule and is unauthenticated.
663
+ # That is the customer's to manage, and it is documented where they will see it
664
+ # rather than only here — a recipient who needs an attachment to outlive that
665
+ # window copies it on receipt.
666
+ #
667
+ # Storing a presigned URL is the specific mistake this shape still avoids:
668
+ # M260826130000 and M260826140000 exist because RCS assets were stored as signed
669
+ # URLs and went stale. Nothing here is signed.
591
670
  sig do
592
- params(media_type: T.nilable(String), url: String).returns(
593
- T.attached_class
594
- )
671
+ params(
672
+ media_type: T.nilable(String),
673
+ mime_type: T.nilable(String),
674
+ size_bytes: T.nilable(Integer),
675
+ source_hash_sha256: T.nilable(String),
676
+ url: T.nilable(String)
677
+ ).returns(T.attached_class)
595
678
  end
596
679
  def self.new(
597
- # One of Constants.MmsMediaTypes when known. Advisory — the carrier reads the
598
- # fetched object's Content-Type, not this.
680
+ # One of MmsMediaTypes when the content type is known. Advisory — a reader should
681
+ # trust the fetched object's own Content-Type.
599
682
  media_type: nil,
683
+ # Content type as the provider declared it. Null when it declared none.
684
+ mime_type: nil,
685
+ # Size as the provider declared it. Never measured here — nothing downloads the
686
+ # file.
687
+ size_bytes: nil,
688
+ # Inbound only: the SHA-256 the provider declared alongside the attachment, when
689
+ # it declared one. Relayed to the customer so they can verify what they fetch
690
+ # matches what the carrier said it sent. It is the only integrity signal available
691
+ # on an attachment nobody here has read.
692
+ source_hash_sha256: nil,
693
+ # Where the file lives. Outbound: the URL the customer gave us and the carrier
694
+ # fetched. Inbound: the URL the carrier hosts it at, relayed unchanged.
600
695
  url: nil
601
696
  )
602
697
  end
603
698
 
604
699
  sig do
605
- override.returns({ media_type: T.nilable(String), url: String })
700
+ override.returns(
701
+ {
702
+ media_type: T.nilable(String),
703
+ mime_type: T.nilable(String),
704
+ size_bytes: T.nilable(Integer),
705
+ source_hash_sha256: T.nilable(String),
706
+ url: T.nilable(String)
707
+ }
708
+ )
606
709
  end
607
710
  def to_hash
608
711
  end
@@ -23,13 +23,28 @@ module Sentdm
23
23
  sig { params(account_id: String).void }
24
24
  attr_writer :account_id
25
25
 
26
- # The channel the message arrived on, for example sms or whatsapp.
26
+ # The channel the message arrived on, for example sms or mms.
27
27
  sig { returns(T.nilable(String)) }
28
28
  attr_reader :channel
29
29
 
30
30
  sig { params(channel: String).void }
31
31
  attr_writer :channel
32
32
 
33
+ # Attachments the contact sent, present only on channels that carry them (mms
34
+ # today) and omitted entirely otherwise.
35
+ #
36
+ # Each url points at the carrier's own copy of the file — sent.dm records where
37
+ # the attachment is, not the attachment itself. The link is unauthenticated and
38
+ # expires on the carrier's schedule, which differs between them: assume days, not
39
+ # months. Download what you need on receipt; re-reading the message through GET
40
+ # /v3/messages/{id} returns the same stored link, not a fresh one, so once it
41
+ # lapses the entry remains with whatever the carrier declared about the file but
42
+ # the file is no longer reachable.
43
+ sig do
44
+ returns(T.nilable(T::Array[Sentdm::InboundMessageEventPayload::Media]))
45
+ end
46
+ attr_accessor :media
47
+
33
48
  # The inbound message.
34
49
  sig { returns(T.nilable(String)) }
35
50
  attr_reader :message_id
@@ -66,6 +81,10 @@ module Sentdm
66
81
  received_at: String,
67
82
  account_id: String,
68
83
  channel: String,
84
+ media:
85
+ T.nilable(
86
+ T::Array[Sentdm::InboundMessageEventPayload::Media::OrHash]
87
+ ),
69
88
  message_id: String,
70
89
  outbound_number: String,
71
90
  text: T.nilable(String),
@@ -79,8 +98,19 @@ module Sentdm
79
98
  received_at:,
80
99
  # The account the message belongs to.
81
100
  account_id: nil,
82
- # The channel the message arrived on, for example sms or whatsapp.
101
+ # The channel the message arrived on, for example sms or mms.
83
102
  channel: nil,
103
+ # Attachments the contact sent, present only on channels that carry them (mms
104
+ # today) and omitted entirely otherwise.
105
+ #
106
+ # Each url points at the carrier's own copy of the file — sent.dm records where
107
+ # the attachment is, not the attachment itself. The link is unauthenticated and
108
+ # expires on the carrier's schedule, which differs between them: assume days, not
109
+ # months. Download what you need on receipt; re-reading the message through GET
110
+ # /v3/messages/{id} returns the same stored link, not a fresh one, so once it
111
+ # lapses the entry remains with whatever the carrier declared about the file but
112
+ # the file is no longer reachable.
113
+ media: nil,
84
114
  # The inbound message.
85
115
  message_id: nil,
86
116
  # Your number in E.164 format, meaning the number the message was addressed to.
@@ -102,6 +132,8 @@ module Sentdm
102
132
  received_at: String,
103
133
  account_id: String,
104
134
  channel: String,
135
+ media:
136
+ T.nilable(T::Array[Sentdm::InboundMessageEventPayload::Media]),
105
137
  message_id: String,
106
138
  outbound_number: String,
107
139
  text: T.nilable(String),
@@ -111,6 +143,82 @@ module Sentdm
111
143
  end
112
144
  def to_hash
113
145
  end
146
+
147
+ class Media < Sentdm::Internal::Type::BaseModel
148
+ OrHash =
149
+ T.type_alias do
150
+ T.any(
151
+ Sentdm::InboundMessageEventPayload::Media,
152
+ Sentdm::Internal::AnyHash
153
+ )
154
+ end
155
+
156
+ # SHA-256 of the file as the carrier declared it, when it declares one. Verify
157
+ # what you download against this — sent.dm never reads the bytes, so it is the
158
+ # only integrity signal available.
159
+ sig { returns(T.nilable(String)) }
160
+ attr_accessor :hash_sha256
161
+
162
+ # Content type as the carrier reported it, for example image/jpeg.
163
+ sig { returns(T.nilable(String)) }
164
+ attr_accessor :mime_type
165
+
166
+ # Size in bytes as the carrier declared it. Absent when it declared none.
167
+ sig { returns(T.nilable(Integer)) }
168
+ attr_accessor :size_bytes
169
+
170
+ # Where the carrier hosts the attachment.
171
+ #
172
+ # This link expires and is not authenticated. sent.dm relays it rather than
173
+ # copying the file, so how long it stays fetchable is the carrier's decision and
174
+ # differs between them — assume days, not months. Anyone holding the URL can fetch
175
+ # it until it lapses. Copy the file on receipt if you need it to outlive that
176
+ # window; do not store this URL as a permanent reference.
177
+ sig { returns(T.nilable(String)) }
178
+ attr_accessor :url
179
+
180
+ # One attachment on an inbound message.
181
+ sig do
182
+ params(
183
+ hash_sha256: T.nilable(String),
184
+ mime_type: T.nilable(String),
185
+ size_bytes: T.nilable(Integer),
186
+ url: T.nilable(String)
187
+ ).returns(T.attached_class)
188
+ end
189
+ def self.new(
190
+ # SHA-256 of the file as the carrier declared it, when it declares one. Verify
191
+ # what you download against this — sent.dm never reads the bytes, so it is the
192
+ # only integrity signal available.
193
+ hash_sha256: nil,
194
+ # Content type as the carrier reported it, for example image/jpeg.
195
+ mime_type: nil,
196
+ # Size in bytes as the carrier declared it. Absent when it declared none.
197
+ size_bytes: nil,
198
+ # Where the carrier hosts the attachment.
199
+ #
200
+ # This link expires and is not authenticated. sent.dm relays it rather than
201
+ # copying the file, so how long it stays fetchable is the carrier's decision and
202
+ # differs between them — assume days, not months. Anyone holding the URL can fetch
203
+ # it until it lapses. Copy the file on receipt if you need it to outlive that
204
+ # window; do not store this URL as a permanent reference.
205
+ url: nil
206
+ )
207
+ end
208
+
209
+ sig do
210
+ override.returns(
211
+ {
212
+ hash_sha256: T.nilable(String),
213
+ mime_type: T.nilable(String),
214
+ size_bytes: T.nilable(Integer),
215
+ url: T.nilable(String)
216
+ }
217
+ )
218
+ end
219
+ def to_hash
220
+ end
221
+ end
114
222
  end
115
223
  end
116
224
  end
@@ -123,6 +123,25 @@ module Sentdm
123
123
  sig { returns(T.nilable(String)) }
124
124
  attr_accessor :email
125
125
 
126
+ # Whether this account may mark a template for automatic creation on its sender
127
+ # profiles. Granted by Sent per account and off by default, so it is what a
128
+ # template-create form reads to decide whether to offer the option at all —
129
+ # marking a template without it does nothing.
130
+ #
131
+ # Top-level rather than inside settings, which is written for type: "profile" only
132
+ # and so would never carry it to the account type that can act on it.
133
+ #
134
+ # This is the capability, not the stored flag: a profile reports false whatever
135
+ # its own row holds. A sender profile owns no sender profiles, so a template it
136
+ # marked would have nothing to be created on and the fan-out would never read the
137
+ # flag. The admin GET /customers/{id} reports the stored value instead, because
138
+ # that is the one an operator granted.
139
+ sig { returns(T.nilable(T::Boolean)) }
140
+ attr_reader :enable_template_auto_creation_for_sp
141
+
142
+ sig { params(enable_template_auto_creation_for_sp: T::Boolean).void }
143
+ attr_writer :enable_template_auto_creation_for_sp
144
+
126
145
  # Account icon URL
127
146
  sig { returns(T.nilable(String)) }
128
147
  attr_accessor :icon
@@ -218,6 +237,7 @@ module Sentdm
218
237
  created_at: Time,
219
238
  description: T.nilable(String),
220
239
  email: T.nilable(String),
240
+ enable_template_auto_creation_for_sp: T::Boolean,
221
241
  icon: T.nilable(String),
222
242
  name: String,
223
243
  organization_id: T.nilable(String),
@@ -245,6 +265,20 @@ module Sentdm
245
265
  description: nil,
246
266
  # Contact email address
247
267
  email: nil,
268
+ # Whether this account may mark a template for automatic creation on its sender
269
+ # profiles. Granted by Sent per account and off by default, so it is what a
270
+ # template-create form reads to decide whether to offer the option at all —
271
+ # marking a template without it does nothing.
272
+ #
273
+ # Top-level rather than inside settings, which is written for type: "profile" only
274
+ # and so would never carry it to the account type that can act on it.
275
+ #
276
+ # This is the capability, not the stored flag: a profile reports false whatever
277
+ # its own row holds. A sender profile owns no sender profiles, so a template it
278
+ # marked would have nothing to be created on and the fan-out would never read the
279
+ # flag. The admin GET /customers/{id} reports the stored value instead, because
280
+ # that is the one an operator granted.
281
+ enable_template_auto_creation_for_sp: nil,
248
282
  # Account icon URL
249
283
  icon: nil,
250
284
  # Account name
@@ -293,6 +327,7 @@ module Sentdm
293
327
  created_at: Time,
294
328
  description: T.nilable(String),
295
329
  email: T.nilable(String),
330
+ enable_template_auto_creation_for_sp: T::Boolean,
296
331
  icon: T.nilable(String),
297
332
  name: String,
298
333
  organization_id: T.nilable(String),
@@ -54,6 +54,20 @@ module Sentdm
54
54
  sig { params(outbound_number: String).void }
55
55
  attr_writer :outbound_number
56
56
 
57
+ # A human-readable sentence for ReasonCode, for example "The recipient is not
58
+ # registered on this channel". Omitted whenever reason_code is.
59
+ sig { returns(T.nilable(String)) }
60
+ attr_accessor :reason
61
+
62
+ # Why the message reached this status, as a stable platform code such as
63
+ # DELIVERY_007 or BUSINESS_003. Present on message.failed, message.filtered and
64
+ # message.blocked; omitted on every status that needs no explanation. Switch on
65
+ # this rather than on Reason: the code is stable, the wording may be improved. It
66
+ # is the platform's classification of the outcome and never a carrier or vendor
67
+ # code.
68
+ sig { returns(T.nilable(String)) }
69
+ attr_accessor :reason_code
70
+
57
71
  # message.scheduled only: why the message is held, either because you scheduled it
58
72
  # or because the recipient is inside a protected quiet-hours window. Omitted on
59
73
  # every other event.
@@ -93,6 +107,8 @@ module Sentdm
93
107
  channel: String,
94
108
  message_id: String,
95
109
  outbound_number: String,
110
+ reason: T.nilable(String),
111
+ reason_code: T.nilable(String),
96
112
  schedule_reason: T.nilable(String),
97
113
  scheduled_at: T.nilable(String),
98
114
  template_id: T.nilable(String),
@@ -121,6 +137,16 @@ module Sentdm
121
137
  message_id: nil,
122
138
  # The recipient's number in E.164 format.
123
139
  outbound_number: nil,
140
+ # A human-readable sentence for ReasonCode, for example "The recipient is not
141
+ # registered on this channel". Omitted whenever reason_code is.
142
+ reason: nil,
143
+ # Why the message reached this status, as a stable platform code such as
144
+ # DELIVERY_007 or BUSINESS_003. Present on message.failed, message.filtered and
145
+ # message.blocked; omitted on every status that needs no explanation. Switch on
146
+ # this rather than on Reason: the code is stable, the wording may be improved. It
147
+ # is the platform's classification of the outcome and never a carrier or vendor
148
+ # code.
149
+ reason_code: nil,
124
150
  # message.scheduled only: why the message is held, either because you scheduled it
125
151
  # or because the recipient is inside a protected quiet-hours window. Omitted on
126
152
  # every other event.
@@ -148,6 +174,8 @@ module Sentdm
148
174
  channel: String,
149
175
  message_id: String,
150
176
  outbound_number: String,
177
+ reason: T.nilable(String),
178
+ reason_code: T.nilable(String),
151
179
  schedule_reason: T.nilable(String),
152
180
  scheduled_at: T.nilable(String),
153
181
  template_id: T.nilable(String),
@@ -203,6 +203,19 @@ module Sentdm
203
203
  sig { returns(T.nilable(String)) }
204
204
  attr_accessor :price
205
205
 
206
+ # A human-readable sentence for reason_code, for example "The recipient is not
207
+ # registered on this channel" Omitted whenever reason_code is.
208
+ sig { returns(T.nilable(String)) }
209
+ attr_accessor :reason
210
+
211
+ # Why the message reached this status, as a stable platform code such as
212
+ # DELIVERY_007 or BUSINESS_003. Present on FAILED, FILTERED and BLOCKED
213
+ # activities; omitted on every status that needs no explanation. Switch on this
214
+ # rather than on reason: the code is stable, the wording may be improved. Same
215
+ # wire name and vocabulary as on the message and the webhook.
216
+ sig { returns(T.nilable(String)) }
217
+ attr_accessor :reason_code
218
+
206
219
  # SCHEDULED activities only: when the held message will be released for delivery,
207
220
  # in UTC. Same wire name as on the send response, the message and the webhook.
208
221
  # Omitted on every other activity. A message that quiet hours moved at release has
@@ -236,6 +249,8 @@ module Sentdm
236
249
  description: String,
237
250
  from: T.nilable(String),
238
251
  price: T.nilable(String),
252
+ reason: T.nilable(String),
253
+ reason_code: T.nilable(String),
239
254
  scheduled_at: T.nilable(Time),
240
255
  status: String,
241
256
  timestamp: Time
@@ -254,6 +269,15 @@ module Sentdm
254
269
  # Channel cost for this activity (e.g., SMS/WhatsApp provider cost), formatted to
255
270
  # 4 decimal places.
256
271
  price: nil,
272
+ # A human-readable sentence for reason_code, for example "The recipient is not
273
+ # registered on this channel" Omitted whenever reason_code is.
274
+ reason: nil,
275
+ # Why the message reached this status, as a stable platform code such as
276
+ # DELIVERY_007 or BUSINESS_003. Present on FAILED, FILTERED and BLOCKED
277
+ # activities; omitted on every status that needs no explanation. Switch on this
278
+ # rather than on reason: the code is stable, the wording may be improved. Same
279
+ # wire name and vocabulary as on the message and the webhook.
280
+ reason_code: nil,
257
281
  # SCHEDULED activities only: when the held message will be released for delivery,
258
282
  # in UTC. Same wire name as on the send response, the message and the webhook.
259
283
  # Omitted on every other activity. A message that quiet hours moved at release has
@@ -274,6 +298,8 @@ module Sentdm
274
298
  description: String,
275
299
  from: T.nilable(String),
276
300
  price: T.nilable(String),
301
+ reason: T.nilable(String),
302
+ reason_code: T.nilable(String),
277
303
  scheduled_at: T.nilable(Time),
278
304
  status: String,
279
305
  timestamp: Time