sentdm 0.31.0 → 0.33.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/README.md +1 -1
  4. data/lib/sentdm/client.rb +6 -0
  5. data/lib/sentdm/models/channel_event.rb +78 -0
  6. data/lib/sentdm/models/channel_event_payload.rb +265 -0
  7. data/lib/sentdm/models/contact_event.rb +77 -0
  8. data/lib/sentdm/models/contact_event_payload.rb +172 -0
  9. data/lib/sentdm/models/conversation_messages_list.rb +125 -4
  10. data/lib/sentdm/models/inbound_message_event.rb +16 -8
  11. data/lib/sentdm/models/message_event.rb +16 -8
  12. data/lib/sentdm/models/message_event_payload.rb +30 -1
  13. data/lib/sentdm/models/message_retrieve_activities_response.rb +20 -5
  14. data/lib/sentdm/models/message_retrieve_status_response.rb +133 -6
  15. data/lib/sentdm/models/message_send_params.rb +67 -3
  16. data/lib/sentdm/models/message_send_response.rb +13 -4
  17. data/lib/sentdm/models/template.rb +36 -5
  18. data/lib/sentdm/models/template_body.rb +102 -12
  19. data/lib/sentdm/models/template_body_content.rb +35 -3
  20. data/lib/sentdm/models/template_button.rb +8 -2
  21. data/lib/sentdm/models/template_button_props.rb +12 -1
  22. data/lib/sentdm/models/template_definition.rb +14 -2
  23. data/lib/sentdm/models/template_event.rb +16 -8
  24. data/lib/sentdm/models/template_event_payload.rb +31 -4
  25. data/lib/sentdm/models/template_header.rb +91 -3
  26. data/lib/sentdm/models/template_variable.rb +35 -4
  27. data/lib/sentdm/models/webhook_list_events_response.rb +332 -9
  28. data/lib/sentdm/models.rb +8 -0
  29. data/lib/sentdm/resources/messages.rb +33 -5
  30. data/lib/sentdm/resources/templates.rb +28 -2
  31. data/lib/sentdm/version.rb +1 -1
  32. data/lib/sentdm.rb +5 -1
  33. data/rbi/sentdm/client.rbi +6 -0
  34. data/rbi/sentdm/models/channel_event.rbi +128 -0
  35. data/rbi/sentdm/models/channel_event_payload.rbi +440 -0
  36. data/rbi/sentdm/models/contact_event.rbi +126 -0
  37. data/rbi/sentdm/models/contact_event_payload.rbi +254 -0
  38. data/rbi/sentdm/models/conversation_messages_list.rbi +202 -7
  39. data/rbi/sentdm/models/inbound_message_event.rbi +18 -10
  40. data/rbi/sentdm/models/message_event.rbi +18 -10
  41. data/rbi/sentdm/models/message_event_payload.rbi +34 -0
  42. data/rbi/sentdm/models/message_retrieve_activities_response.rbi +23 -5
  43. data/rbi/sentdm/models/message_retrieve_status_response.rbi +214 -9
  44. data/rbi/sentdm/models/message_send_params.rbi +100 -2
  45. data/rbi/sentdm/models/message_send_response.rbi +15 -5
  46. data/rbi/sentdm/models/template.rbi +58 -4
  47. data/rbi/sentdm/models/template_body.rbi +155 -13
  48. data/rbi/sentdm/models/template_body_content.rbi +59 -1
  49. data/rbi/sentdm/models/template_button.rbi +14 -2
  50. data/rbi/sentdm/models/template_button_props.rbi +22 -0
  51. data/rbi/sentdm/models/template_definition.rbi +20 -2
  52. data/rbi/sentdm/models/template_event.rbi +18 -10
  53. data/rbi/sentdm/models/template_event_payload.rbi +51 -8
  54. data/rbi/sentdm/models/template_header.rbi +143 -2
  55. data/rbi/sentdm/models/template_variable.rbi +38 -1
  56. data/rbi/sentdm/models/webhook_list_events_response.rbi +484 -12
  57. data/rbi/sentdm/models.rbi +8 -0
  58. data/rbi/sentdm/resources/messages.rbi +58 -3
  59. data/rbi/sentdm/resources/templates.rbi +28 -2
  60. data/sig/sentdm/models/channel_event.rbs +44 -0
  61. data/sig/sentdm/models/channel_event_payload.rbs +120 -0
  62. data/sig/sentdm/models/contact_event.rbs +44 -0
  63. data/sig/sentdm/models/contact_event_payload.rbs +78 -0
  64. data/sig/sentdm/models/conversation_messages_list.rbs +48 -3
  65. data/sig/sentdm/models/inbound_message_event.rbs +5 -0
  66. data/sig/sentdm/models/message_event.rbs +5 -0
  67. data/sig/sentdm/models/message_event_payload.rbs +15 -0
  68. data/sig/sentdm/models/message_retrieve_activities_response.rbs +5 -0
  69. data/sig/sentdm/models/message_retrieve_status_response.rbs +48 -3
  70. data/sig/sentdm/models/message_send_params.rbs +15 -0
  71. data/sig/sentdm/models/template.rbs +5 -0
  72. data/sig/sentdm/models/template_body.rbs +44 -0
  73. data/sig/sentdm/models/template_event.rbs +5 -0
  74. data/sig/sentdm/models/template_event_payload.rbs +9 -6
  75. data/sig/sentdm/models/template_header.rbs +44 -0
  76. data/sig/sentdm/models/webhook_list_events_response.rbs +147 -0
  77. data/sig/sentdm/models.rbs +8 -0
  78. data/sig/sentdm/resources/messages.rbs +3 -0
  79. metadata +14 -2
@@ -39,9 +39,22 @@ module Sentdm
39
39
  attr_accessor :error_message
40
40
 
41
41
  # The exact event body that was delivered, or attempted, for this record. One of
42
- # the three webhook envelopes: a message status change, an inbound message, or a
43
- # template status change. Read field and event to tell which, the same way your
44
- # endpoint does.
42
+ # the six webhook envelopes:
43
+ #
44
+ # message — an outbound message changed status. message with event:
45
+ # message.received — someone replied to you. templates — a template was approved,
46
+ # rejected, paused or similar. channel — one of your markets moved in provisioning
47
+ # or compliance. contact — a consent signal: opt-in, opt-out or help. link — a
48
+ # tracked short link was clicked or a hosted file downloaded, or one expired or
49
+ # was revoked.
50
+ #
51
+ # Read field and event to tell which, the same way your endpoint does. The two
52
+ # message envelopes are the reason that is two fields and not one: they share a
53
+ # field and differ by event.
54
+ #
55
+ # Treat the list as open. It has grown twice — channel and then link — and a
56
+ # handler that rejects an envelope it does not recognise will break on the next
57
+ # addition rather than ignore it.
45
58
  sig do
46
59
  returns(
47
60
  T.nilable(
@@ -57,7 +70,10 @@ module Sentdm
57
70
  T.any(
58
71
  Sentdm::MessageEvent::OrHash,
59
72
  Sentdm::InboundMessageEvent::OrHash,
60
- Sentdm::TemplateEvent::OrHash
73
+ Sentdm::TemplateEvent::OrHash,
74
+ Sentdm::ChannelEvent::OrHash,
75
+ Sentdm::ContactEvent::OrHash,
76
+ Sentdm::Models::WebhookListEventsResponse::EventData::SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload::OrHash
61
77
  )
62
78
  ).void
63
79
  end
@@ -92,7 +108,10 @@ module Sentdm
92
108
  T.any(
93
109
  Sentdm::MessageEvent::OrHash,
94
110
  Sentdm::InboundMessageEvent::OrHash,
95
- Sentdm::TemplateEvent::OrHash
111
+ Sentdm::TemplateEvent::OrHash,
112
+ Sentdm::ChannelEvent::OrHash,
113
+ Sentdm::ContactEvent::OrHash,
114
+ Sentdm::Models::WebhookListEventsResponse::EventData::SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload::OrHash
96
115
  ),
97
116
  event_type: String,
98
117
  http_status_code: T.nilable(Integer),
@@ -108,9 +127,22 @@ module Sentdm
108
127
  delivery_status: nil,
109
128
  error_message: nil,
110
129
  # The exact event body that was delivered, or attempted, for this record. One of
111
- # the three webhook envelopes: a message status change, an inbound message, or a
112
- # template status change. Read field and event to tell which, the same way your
113
- # endpoint does.
130
+ # the six webhook envelopes:
131
+ #
132
+ # message — an outbound message changed status. message with event:
133
+ # message.received — someone replied to you. templates — a template was approved,
134
+ # rejected, paused or similar. channel — one of your markets moved in provisioning
135
+ # or compliance. contact — a consent signal: opt-in, opt-out or help. link — a
136
+ # tracked short link was clicked or a hosted file downloaded, or one expired or
137
+ # was revoked.
138
+ #
139
+ # Read field and event to tell which, the same way your endpoint does. The two
140
+ # message envelopes are the reason that is two fields and not one: they share a
141
+ # field and differ by event.
142
+ #
143
+ # Treat the list as open. It has grown twice — channel and then link — and a
144
+ # handler that rejects an envelope it does not recognise will break on the next
145
+ # addition rather than ignore it.
114
146
  event_data: nil,
115
147
  event_type: nil,
116
148
  http_status_code: nil,
@@ -142,9 +174,22 @@ module Sentdm
142
174
  end
143
175
 
144
176
  # The exact event body that was delivered, or attempted, for this record. One of
145
- # the three webhook envelopes: a message status change, an inbound message, or a
146
- # template status change. Read field and event to tell which, the same way your
147
- # endpoint does.
177
+ # the six webhook envelopes:
178
+ #
179
+ # message — an outbound message changed status. message with event:
180
+ # message.received — someone replied to you. templates — a template was approved,
181
+ # rejected, paused or similar. channel — one of your markets moved in provisioning
182
+ # or compliance. contact — a consent signal: opt-in, opt-out or help. link — a
183
+ # tracked short link was clicked or a hosted file downloaded, or one expired or
184
+ # was revoked.
185
+ #
186
+ # Read field and event to tell which, the same way your endpoint does. The two
187
+ # message envelopes are the reason that is two fields and not one: they share a
188
+ # field and differ by event.
189
+ #
190
+ # Treat the list as open. It has grown twice — channel and then link — and a
191
+ # handler that rejects an envelope it does not recognise will break on the next
192
+ # addition rather than ignore it.
148
193
  module EventData
149
194
  extend Sentdm::Internal::Type::Union
150
195
 
@@ -153,10 +198,437 @@ module Sentdm
153
198
  T.any(
154
199
  Sentdm::MessageEvent,
155
200
  Sentdm::InboundMessageEvent,
156
- Sentdm::TemplateEvent
201
+ Sentdm::TemplateEvent,
202
+ Sentdm::ChannelEvent,
203
+ Sentdm::ContactEvent,
204
+ Sentdm::Models::WebhookListEventsResponse::EventData::SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload
157
205
  )
158
206
  end
159
207
 
208
+ class SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload < Sentdm::Internal::Type::BaseModel
209
+ OrHash =
210
+ T.type_alias do
211
+ T.any(
212
+ Sentdm::Models::WebhookListEventsResponse::EventData::SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload,
213
+ Sentdm::Internal::AnyHash
214
+ )
215
+ end
216
+
217
+ # The specific event within the family, for example message.delivered,
218
+ # message.received or contact.opt_out. Absent on events that have no subtype, so
219
+ # treat it as optional.
220
+ sig { returns(T.nilable(String)) }
221
+ attr_accessor :event
222
+
223
+ # The event family, for example message, templates or contact. Route on this
224
+ # first, then on event for the specific change.
225
+ sig { returns(T.nilable(String)) }
226
+ attr_reader :field
227
+
228
+ sig { params(field: String).void }
229
+ attr_writer :field
230
+
231
+ # Body of a link event: something happened to a tracked link Sent published on the
232
+ # customer's behalf. A link points either at a URL the customer supplied or at a
233
+ # file Sent hosts for them; LinkKind says which. Delivered when an eligible
234
+ # request is served, or when a published link reaches the end of its life.
235
+ #
236
+ # A click is a request, not a read receipt. link.clicked means the redirect was
237
+ # served; link.downloaded means bytes went out. Neither proves a person saw
238
+ # anything — messaging providers and link scanners fetch URLs on their own, which
239
+ # is what TrafficClass exists to tell apart. Filter on it before reporting a
240
+ # click-through rate; treat likely_human as a hint, never as delivery
241
+ # confirmation.
242
+ #
243
+ # RecordId identifies the link; the X-Webhook-Event-ID header identifies the
244
+ # delivery. One link is hit many times, so those are the two keys a subscriber
245
+ # needs: group by the first, deduplicate on the second — exactly as on every other
246
+ # family. The payload carries no event identifier of its own, for the same reason
247
+ # none of the others do.
248
+ #
249
+ # Nothing here identifies the visitor. No IP address and no visitor token crosses
250
+ # this boundary. Country, Device and Browser are coarse buckets derived at the
251
+ # edge and are absent whenever the request did not supply enough to derive them.
252
+ sig do
253
+ returns(
254
+ T.nilable(
255
+ Sentdm::Models::WebhookListEventsResponse::EventData::SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload::Payload
256
+ )
257
+ )
258
+ end
259
+ attr_reader :payload
260
+
261
+ sig do
262
+ params(
263
+ payload:
264
+ T.nilable(
265
+ Sentdm::Models::WebhookListEventsResponse::EventData::SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload::Payload::OrHash
266
+ )
267
+ ).void
268
+ end
269
+ attr_writer :payload
270
+
271
+ # The event-specific body.
272
+ sig { returns(T.nilable(String)) }
273
+ attr_accessor :request_id
274
+
275
+ # When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
276
+ # time, not the time the underlying change happened. Use the timestamp inside the
277
+ # payload for the latter.
278
+ sig { returns(T.nilable(String)) }
279
+ attr_reader :timestamp
280
+
281
+ sig { params(timestamp: String).void }
282
+ attr_writer :timestamp
283
+
284
+ # The envelope Sent POSTs to a subscribed webhook endpoint. Every event shares
285
+ # this shape and varies only in Payload.
286
+ sig do
287
+ params(
288
+ event: T.nilable(String),
289
+ field: String,
290
+ payload:
291
+ T.nilable(
292
+ Sentdm::Models::WebhookListEventsResponse::EventData::SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload::Payload::OrHash
293
+ ),
294
+ request_id: T.nilable(String),
295
+ timestamp: String
296
+ ).returns(T.attached_class)
297
+ end
298
+ def self.new(
299
+ # The specific event within the family, for example message.delivered,
300
+ # message.received or contact.opt_out. Absent on events that have no subtype, so
301
+ # treat it as optional.
302
+ event: nil,
303
+ # The event family, for example message, templates or contact. Route on this
304
+ # first, then on event for the specific change.
305
+ field: nil,
306
+ # Body of a link event: something happened to a tracked link Sent published on the
307
+ # customer's behalf. A link points either at a URL the customer supplied or at a
308
+ # file Sent hosts for them; LinkKind says which. Delivered when an eligible
309
+ # request is served, or when a published link reaches the end of its life.
310
+ #
311
+ # A click is a request, not a read receipt. link.clicked means the redirect was
312
+ # served; link.downloaded means bytes went out. Neither proves a person saw
313
+ # anything — messaging providers and link scanners fetch URLs on their own, which
314
+ # is what TrafficClass exists to tell apart. Filter on it before reporting a
315
+ # click-through rate; treat likely_human as a hint, never as delivery
316
+ # confirmation.
317
+ #
318
+ # RecordId identifies the link; the X-Webhook-Event-ID header identifies the
319
+ # delivery. One link is hit many times, so those are the two keys a subscriber
320
+ # needs: group by the first, deduplicate on the second — exactly as on every other
321
+ # family. The payload carries no event identifier of its own, for the same reason
322
+ # none of the others do.
323
+ #
324
+ # Nothing here identifies the visitor. No IP address and no visitor token crosses
325
+ # this boundary. Country, Device and Browser are coarse buckets derived at the
326
+ # edge and are absent whenever the request did not supply enough to derive them.
327
+ payload: nil,
328
+ # The event-specific body.
329
+ request_id: nil,
330
+ # When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
331
+ # time, not the time the underlying change happened. Use the timestamp inside the
332
+ # payload for the latter.
333
+ timestamp: nil
334
+ )
335
+ end
336
+
337
+ sig do
338
+ override.returns(
339
+ {
340
+ event: T.nilable(String),
341
+ field: String,
342
+ payload:
343
+ T.nilable(
344
+ Sentdm::Models::WebhookListEventsResponse::EventData::SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload::Payload
345
+ ),
346
+ request_id: T.nilable(String),
347
+ timestamp: String
348
+ }
349
+ )
350
+ end
351
+ def to_hash
352
+ end
353
+
354
+ class Payload < Sentdm::Internal::Type::BaseModel
355
+ OrHash =
356
+ T.type_alias do
357
+ T.any(
358
+ Sentdm::Models::WebhookListEventsResponse::EventData::SentDmServicesCommonServicesWebhooksContractsWebhookEventOfLinkWebhookPayload::Payload,
359
+ Sentdm::Internal::AnyHash
360
+ )
361
+ end
362
+
363
+ # The link's public identifier — the eight-character code in the short URL, for
364
+ # example A78B2BU0. Unique across both kinds, and never reused, so it is the
365
+ # stable key to group one link's events by.
366
+ sig { returns(String) }
367
+ attr_accessor :record_id
368
+
369
+ # Where the request appeared to come from, as an ISO 3166-1 alpha-2 code. Named
370
+ # separately from the country on a channel event, which is a destination market
371
+ # the customer registered for — this one is a property of a single visitor and is
372
+ # absent when the edge could not resolve it.
373
+ sig { returns(T.nilable(String)) }
374
+ attr_accessor :access_country
375
+
376
+ # How the request was served, when the edge recorded it. Free text describing the
377
+ # outcome — show it to a human rather than branching on it.
378
+ sig { returns(T.nilable(String)) }
379
+ attr_accessor :access_outcome
380
+
381
+ # The requesting browser family, for example chrome or safari, or unknown. Derived
382
+ # from the user agent.
383
+ sig { returns(T.nilable(String)) }
384
+ attr_accessor :browser
385
+
386
+ # How many bytes were served, for a file access. A ranged request reports the
387
+ # bytes in that range, not the size of the file, so several accesses of one file
388
+ # can each report a part.
389
+ sig { returns(T.nilable(Integer)) }
390
+ attr_accessor :bytes_served
391
+
392
+ # The channel the message carrying this link went out on: sms, whatsapp, or rcs.
393
+ sig { returns(T.nilable(String)) }
394
+ attr_accessor :channel
395
+
396
+ # The organization the link belongs to. Always the parent account, never a sender
397
+ # profile — read SenderProfileId for that.
398
+ #
399
+ # This family publishes the owner as an explicit pair rather than the single
400
+ # account_id the other families use. The pair says which organization and which
401
+ # profile without the subscriber deriving either, which is the trade: one more key
402
+ # against not having to know that account_id silently becomes the profile when one
403
+ # exists.
404
+ sig { returns(T.nilable(String)) }
405
+ attr_reader :customer_id
406
+
407
+ sig { params(customer_id: String).void }
408
+ attr_writer :customer_id
409
+
410
+ # The requesting device class: mobile, tablet, desktop or unknown. Derived from
411
+ # the user agent.
412
+ sig { returns(T.nilable(String)) }
413
+ attr_accessor :device
414
+
415
+ # What the link points at: url for a destination the customer supplied, file for
416
+ # media Sent hosts. Always present, and implied by the event — link.clicked is
417
+ # always url and link.downloaded always file — but published as its own field so a
418
+ # subscriber can branch on the kind without parsing the event name, the same
419
+ # separation the channel family keeps between its event and its status.
420
+ sig { returns(T.nilable(String)) }
421
+ attr_reader :link_kind
422
+
423
+ sig { params(link_kind: String).void }
424
+ attr_writer :link_kind
425
+
426
+ # The message the link was published in.
427
+ #
428
+ # The event can arrive before the message is readable through GET /v3/messages: a
429
+ # provider may fetch a link within milliseconds of the send, and nothing here
430
+ # waits for the message row. Retry the read rather than treating an unknown id as
431
+ # an error.
432
+ sig { returns(T.nilable(String)) }
433
+ attr_accessor :message_id
434
+
435
+ # When the access or lifecycle change actually happened, in UTC
436
+ # (yyyy-MM-ddTHH:mm:ssZ). The envelope's timestamp is when Sent emitted the event;
437
+ # this is when the thing occurred, and the two differ by the ingest delay.
438
+ sig { returns(T.nilable(String)) }
439
+ attr_reader :occurred_at
440
+
441
+ sig { params(occurred_at: String).void }
442
+ attr_writer :occurred_at
443
+
444
+ # The caller-supplied label tying this link back to a position in the message, for
445
+ # example body:0 for the first link in the body. Present when the link was created
446
+ # with one.
447
+ sig { returns(T.nilable(String)) }
448
+ attr_accessor :reference_key
449
+
450
+ # The host of the page that linked here, when the request supplied one. The host
451
+ # only — never a full referring URL.
452
+ sig { returns(T.nilable(String)) }
453
+ attr_accessor :referrer_host
454
+
455
+ # The HTTP method of the request that was served, for an access event. Omitted on
456
+ # link.expired and link.revoked, which describe no request.
457
+ sig { returns(T.nilable(String)) }
458
+ attr_accessor :request_method
459
+
460
+ # The sender profile that owns the link, or null when the organization owns it
461
+ # directly. Always on the wire so a handler reads one shape rather than branching
462
+ # on whether the key arrived.
463
+ #
464
+ # sender_profile_id, not profile_id: the API already publishes
465
+ # messaging_profile_id and sending_phone_number_profile_id for provider-side
466
+ # profiles, which are a different thing entirely. The unqualified name would read
467
+ # as one of those.
468
+ sig { returns(T.nilable(String)) }
469
+ attr_accessor :sender_profile_id
470
+
471
+ # The HTTP status Sent answered the request with: 302 for a link, 200 or 206 for a
472
+ # file. Omitted on lifecycle events.
473
+ sig { returns(T.nilable(Integer)) }
474
+ attr_accessor :status_code
475
+
476
+ # A coarse guess at what made the request: likely_human, provider (a messaging
477
+ # platform prefetching the link), bot, or unknown. Derived from the user agent, so
478
+ # it is a hint for filtering noise rather than a fact to bill or report on.
479
+ sig { returns(T.nilable(String)) }
480
+ attr_accessor :traffic_class
481
+
482
+ # Body of a link event: something happened to a tracked link Sent published on the
483
+ # customer's behalf. A link points either at a URL the customer supplied or at a
484
+ # file Sent hosts for them; LinkKind says which. Delivered when an eligible
485
+ # request is served, or when a published link reaches the end of its life.
486
+ #
487
+ # A click is a request, not a read receipt. link.clicked means the redirect was
488
+ # served; link.downloaded means bytes went out. Neither proves a person saw
489
+ # anything — messaging providers and link scanners fetch URLs on their own, which
490
+ # is what TrafficClass exists to tell apart. Filter on it before reporting a
491
+ # click-through rate; treat likely_human as a hint, never as delivery
492
+ # confirmation.
493
+ #
494
+ # RecordId identifies the link; the X-Webhook-Event-ID header identifies the
495
+ # delivery. One link is hit many times, so those are the two keys a subscriber
496
+ # needs: group by the first, deduplicate on the second — exactly as on every other
497
+ # family. The payload carries no event identifier of its own, for the same reason
498
+ # none of the others do.
499
+ #
500
+ # Nothing here identifies the visitor. No IP address and no visitor token crosses
501
+ # this boundary. Country, Device and Browser are coarse buckets derived at the
502
+ # edge and are absent whenever the request did not supply enough to derive them.
503
+ sig do
504
+ params(
505
+ record_id: String,
506
+ access_country: T.nilable(String),
507
+ access_outcome: T.nilable(String),
508
+ browser: T.nilable(String),
509
+ bytes_served: T.nilable(Integer),
510
+ channel: T.nilable(String),
511
+ customer_id: String,
512
+ device: T.nilable(String),
513
+ link_kind: String,
514
+ message_id: T.nilable(String),
515
+ occurred_at: String,
516
+ reference_key: T.nilable(String),
517
+ referrer_host: T.nilable(String),
518
+ request_method: T.nilable(String),
519
+ sender_profile_id: T.nilable(String),
520
+ status_code: T.nilable(Integer),
521
+ traffic_class: T.nilable(String)
522
+ ).returns(T.attached_class)
523
+ end
524
+ def self.new(
525
+ # The link's public identifier — the eight-character code in the short URL, for
526
+ # example A78B2BU0. Unique across both kinds, and never reused, so it is the
527
+ # stable key to group one link's events by.
528
+ record_id:,
529
+ # Where the request appeared to come from, as an ISO 3166-1 alpha-2 code. Named
530
+ # separately from the country on a channel event, which is a destination market
531
+ # the customer registered for — this one is a property of a single visitor and is
532
+ # absent when the edge could not resolve it.
533
+ access_country: nil,
534
+ # How the request was served, when the edge recorded it. Free text describing the
535
+ # outcome — show it to a human rather than branching on it.
536
+ access_outcome: nil,
537
+ # The requesting browser family, for example chrome or safari, or unknown. Derived
538
+ # from the user agent.
539
+ browser: nil,
540
+ # How many bytes were served, for a file access. A ranged request reports the
541
+ # bytes in that range, not the size of the file, so several accesses of one file
542
+ # can each report a part.
543
+ bytes_served: nil,
544
+ # The channel the message carrying this link went out on: sms, whatsapp, or rcs.
545
+ channel: nil,
546
+ # The organization the link belongs to. Always the parent account, never a sender
547
+ # profile — read SenderProfileId for that.
548
+ #
549
+ # This family publishes the owner as an explicit pair rather than the single
550
+ # account_id the other families use. The pair says which organization and which
551
+ # profile without the subscriber deriving either, which is the trade: one more key
552
+ # against not having to know that account_id silently becomes the profile when one
553
+ # exists.
554
+ customer_id: nil,
555
+ # The requesting device class: mobile, tablet, desktop or unknown. Derived from
556
+ # the user agent.
557
+ device: nil,
558
+ # What the link points at: url for a destination the customer supplied, file for
559
+ # media Sent hosts. Always present, and implied by the event — link.clicked is
560
+ # always url and link.downloaded always file — but published as its own field so a
561
+ # subscriber can branch on the kind without parsing the event name, the same
562
+ # separation the channel family keeps between its event and its status.
563
+ link_kind: nil,
564
+ # The message the link was published in.
565
+ #
566
+ # The event can arrive before the message is readable through GET /v3/messages: a
567
+ # provider may fetch a link within milliseconds of the send, and nothing here
568
+ # waits for the message row. Retry the read rather than treating an unknown id as
569
+ # an error.
570
+ message_id: nil,
571
+ # When the access or lifecycle change actually happened, in UTC
572
+ # (yyyy-MM-ddTHH:mm:ssZ). The envelope's timestamp is when Sent emitted the event;
573
+ # this is when the thing occurred, and the two differ by the ingest delay.
574
+ occurred_at: nil,
575
+ # The caller-supplied label tying this link back to a position in the message, for
576
+ # example body:0 for the first link in the body. Present when the link was created
577
+ # with one.
578
+ reference_key: nil,
579
+ # The host of the page that linked here, when the request supplied one. The host
580
+ # only — never a full referring URL.
581
+ referrer_host: nil,
582
+ # The HTTP method of the request that was served, for an access event. Omitted on
583
+ # link.expired and link.revoked, which describe no request.
584
+ request_method: nil,
585
+ # The sender profile that owns the link, or null when the organization owns it
586
+ # directly. Always on the wire so a handler reads one shape rather than branching
587
+ # on whether the key arrived.
588
+ #
589
+ # sender_profile_id, not profile_id: the API already publishes
590
+ # messaging_profile_id and sending_phone_number_profile_id for provider-side
591
+ # profiles, which are a different thing entirely. The unqualified name would read
592
+ # as one of those.
593
+ sender_profile_id: nil,
594
+ # The HTTP status Sent answered the request with: 302 for a link, 200 or 206 for a
595
+ # file. Omitted on lifecycle events.
596
+ status_code: nil,
597
+ # A coarse guess at what made the request: likely_human, provider (a messaging
598
+ # platform prefetching the link), bot, or unknown. Derived from the user agent, so
599
+ # it is a hint for filtering noise rather than a fact to bill or report on.
600
+ traffic_class: nil
601
+ )
602
+ end
603
+
604
+ sig do
605
+ override.returns(
606
+ {
607
+ record_id: String,
608
+ access_country: T.nilable(String),
609
+ access_outcome: T.nilable(String),
610
+ browser: T.nilable(String),
611
+ bytes_served: T.nilable(Integer),
612
+ channel: T.nilable(String),
613
+ customer_id: String,
614
+ device: T.nilable(String),
615
+ link_kind: String,
616
+ message_id: T.nilable(String),
617
+ occurred_at: String,
618
+ reference_key: T.nilable(String),
619
+ referrer_host: T.nilable(String),
620
+ request_method: T.nilable(String),
621
+ sender_profile_id: T.nilable(String),
622
+ status_code: T.nilable(Integer),
623
+ traffic_class: T.nilable(String)
624
+ }
625
+ )
626
+ end
627
+ def to_hash
628
+ end
629
+ end
630
+ end
631
+
160
632
  sig do
161
633
  override.returns(
162
634
  T::Array[
@@ -31,10 +31,18 @@ module Sentdm
31
31
 
32
32
  BrandsBrandData = Sentdm::Models::BrandsBrandData
33
33
 
34
+ ChannelEvent = Sentdm::Models::ChannelEvent
35
+
36
+ ChannelEventPayload = Sentdm::Models::ChannelEventPayload
37
+
34
38
  ContactCreateParams = Sentdm::Models::ContactCreateParams
35
39
 
36
40
  ContactDeleteParams = Sentdm::Models::ContactDeleteParams
37
41
 
42
+ ContactEvent = Sentdm::Models::ContactEvent
43
+
44
+ ContactEventPayload = Sentdm::Models::ContactEventPayload
45
+
38
46
  ContactListParams = Sentdm::Models::ContactListParams
39
47
 
40
48
  ContactMessageSummary = Sentdm::Models::ContactMessageSummary
@@ -13,9 +13,17 @@ module Sentdm
13
13
  # **A message needs a sender.** What you can send, where, and at what cost is
14
14
  # decided by the markets under **Channels** — so a recipient in a country you hold
15
15
  # 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
18
+ # finished message — typically one BLOCKED for insufficient balance — back through
19
+ # the send pipeline. It is a new attempt, not a free retry: every policy runs
20
+ # again, the message is billed again, and its status webhooks fire again. A
21
+ # FILTERED message is never resendable.
16
22
  class Messages
17
23
  # Retrieves the activity log for a specific message. Activities track the message
18
- # lifecycle including acceptance, processing, sending, delivery, and any errors.
24
+ # lifecycle including acceptance, processing, sending, delivery, and any errors. A
25
+ # SCHEDULED entry carries scheduled_at, the release instant in UTC as it stood at
26
+ # that moment. Other entries have no scheduled_at key.
19
27
  sig do
20
28
  params(
21
29
  id: String,
@@ -34,7 +42,11 @@ module Sentdm
34
42
  end
35
43
 
36
44
  # Retrieves the current status and details of a message by ID. Includes delivery
37
- # status, timestamps, and error information if applicable.
45
+ # status, timestamps, and error information if applicable. A message that is or
46
+ # was held for a later time (a send you scheduled with scheduled_at, or a
47
+ # quiet-hours hold) is returned as a ScheduledMessageResponse: the same fields
48
+ # plus scheduled_at, the release instant in UTC. A message sent immediately has no
49
+ # scheduled_at key.
38
50
  sig do
39
51
  params(
40
52
  id: String,
@@ -61,11 +73,24 @@ module Sentdm
61
73
  # insufficient balance, a template not approved for sending, or free-form content
62
74
  # with no open conversation with the contact. The send is accepted with 202 and
63
75
  # the affected messages are reported as BLOCKED on GET /messages/{id} and the
64
- # message.blocked webhook.
76
+ # message.blocked webhook. To send later, set scheduled_at (ISO-8601 with an
77
+ # explicit UTC offset; a value without one is rejected) between 1 minute and 30
78
+ # days ahead: the response is a ScheduledSendMessageResponse (the same fields plus
79
+ # scheduled_at; status is still QUEUED), each message then moves to SCHEDULED, is
80
+ # held and released at that time (within a few minutes), and a message.scheduled
81
+ # webhook fires once it is held. Balance and template approval are evaluated at
82
+ # release, not at acceptance. Quiet hours are not checked when the request is
83
+ # accepted: if the time falls inside a legally protected quiet-hours window for a
84
+ # recipient, that message is moved to the next allowed time at release and a
85
+ # second message.scheduled webhook reports the new scheduled_at. An account may
86
+ # hold at most 1,000,000 scheduled messages at once (429 LIMIT_001).
65
87
  sig do
66
88
  params(
67
89
  channel: T.nilable(T::Array[String]),
90
+ media_urls: T.nilable(T::Array[String]),
68
91
  sandbox: T::Boolean,
92
+ scheduled_at: T.nilable(Time),
93
+ subject: T.nilable(String),
69
94
  template: T.nilable(Sentdm::MessageSendParams::Template::OrHash),
70
95
  text: T.nilable(String),
71
96
  to: T::Array[String],
@@ -79,9 +104,39 @@ module Sentdm
79
104
  # produces a separate message per recipient. "sent" = auto-detect. Defaults to
80
105
  # ["sent"] (auto-detect) if omitted.
81
106
  channel: nil,
107
+ # Body param: Attachments for this send, as publicly fetchable https URLs. Used by
108
+ # the MMS channel and ignored by every other one.
109
+ #
110
+ # Supplying these replaces the media on the template's mms body rather than adding
111
+ # to it, so a template can hold a default creative while a caller still sends
112
+ # something recipient-specific.
113
+ #
114
+ # Their presence is also what makes a message eligible for MMS on an auto-detect
115
+ # send: a message with nothing attached is delivered as SMS, because an MMS with
116
+ # no media is a more expensive text message.
117
+ #
118
+ # The recipient's carrier fetches each URL after the send is accepted, so it must
119
+ # stay publicly reachable — a link that expires, or one behind auth, arrives as a
120
+ # failed message.
121
+ media_urls: nil,
82
122
  # Body param: Sandbox flag - when true, the operation is simulated without side
83
123
  # effects Useful for testing integrations without actual execution
84
124
  sandbox: nil,
125
+ # Body param: Optional future send time as an ISO-8601 timestamp with an explicit
126
+ # UTC offset, e.g. 2026-10-01T09:00:00+02:00 or 2026-10-01T07:00:00Z. A value
127
+ # without an offset is rejected (400) rather than read in the server's zone. The
128
+ # offset only fixes the instant: it is stored and echoed in UTC as scheduled_at.
129
+ # Omit to send now. Must be at least one minute ahead and at most 30 days ahead.
130
+ # Accepted messages report SCHEDULED and are released for delivery at this time.
131
+ # Quiet hours, balance and template approval are evaluated at release, not at
132
+ # acceptance: a message whose time falls inside a recipient's protected
133
+ # quiet-hours window is moved to the next allowed time and a second
134
+ # message.scheduled webhook reports the new scheduled_at.
135
+ scheduled_at: nil,
136
+ # Body param: Subject line for this send, overriding the template's. MMS only;
137
+ # ignored on every other channel. Most handsets render it above the body, some
138
+ # ignore it entirely.
139
+ subject: nil,
85
140
  # Body param: SDK-style template reference: resolve by ID or by name, with
86
141
  # optional parameters.
87
142
  template: nil,