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
@@ -0,0 +1,440 @@
1
+ # typed: strong
2
+
3
+ module Sentdm
4
+ module Models
5
+ class ChannelEventPayload < Sentdm::Internal::Type::BaseModel
6
+ OrHash =
7
+ T.type_alias do
8
+ T.any(Sentdm::ChannelEventPayload, Sentdm::Internal::AnyHash)
9
+ end
10
+
11
+ # The market's destination country as an ISO 3166-1 alpha-2 code, for example XK.
12
+ # Always present, and the property that identifies this payload among the
13
+ # delivered envelopes — see DeliveredWebhookEvents. Every event in this family
14
+ # reports one market, and a market has a country.
15
+ sig { returns(String) }
16
+ attr_accessor :country
17
+
18
+ # The account whose market this is, named as on every other family. When an
19
+ # organization receives an event for one of its sender profiles this is the
20
+ # profile, so a reseller compares it with its own id and anything different is one
21
+ # of its profiles. Matches customer_id on GET /v3/channels and the sender
22
+ # profile's id. Together with channel, country, and number_type, it identifies the
23
+ # market.
24
+ sig { returns(T.nilable(String)) }
25
+ attr_reader :account_id
26
+
27
+ sig { params(account_id: String).void }
28
+ attr_writer :account_id
29
+
30
+ # The channel this market belongs to: sms, whatsapp, or rcs. Never sent — that
31
+ # value belongs to message events, where it names the smart-routing brand rather
32
+ # than a channel that can be provisioned.
33
+ sig { returns(T.nilable(String)) }
34
+ attr_reader :channel
35
+
36
+ sig { params(channel: String).void }
37
+ attr_writer :channel
38
+
39
+ # What a market has been given: the identity it registers under, its programme,
40
+ # and any documents attached.
41
+ #
42
+ # What it does not carry is what the market asks for. That is the subject of GET
43
+ # /v3/compliance/requirements, and it is the same answer for every caller — a
44
+ # description of what a compliance regime wants, not a record of one customer's
45
+ # progress through it. It was reported here as well for a while, which put the
46
+ # same array in six response shapes and left a caller deciding which of two
47
+ # sources to believe.
48
+ #
49
+ # Present on a list read for markets that register (carrying brand and campaign),
50
+ # but with documents absent — documents are not fetched for a list, because a
51
+ # catalog lookup and a document read per market would multiply across a page.
52
+ # Absent documents is distinct from an empty list: absent says they were not
53
+ # fetched; empty says the market has been given none. The parent object is null
54
+ # only when the market registers with nobody and compliance was not computed —
55
+ # nothing to show at all.
56
+ sig { returns(T.nilable(Sentdm::ChannelEventPayload::Compliance)) }
57
+ attr_reader :compliance
58
+
59
+ sig do
60
+ params(
61
+ compliance: T.nilable(Sentdm::ChannelEventPayload::Compliance::OrHash)
62
+ ).void
63
+ end
64
+ attr_writer :compliance
65
+
66
+ # The kind of sender the market uses, for example TEN_DLC, LOCAL, or ALPHANUMERIC.
67
+ # Omitted when the subject has no sender type of its own.
68
+ sig { returns(T.nilable(String)) }
69
+ attr_accessor :number_type
70
+
71
+ # Why the market reached this state, when a reason was given — a correction
72
+ # explained, or a campaign lapse. Free text, passed through from the registry or
73
+ # carrier that wrote it, so treat it as a message to show a human rather than a
74
+ # value to branch on.
75
+ sig { returns(T.nilable(String)) }
76
+ attr_accessor :reason
77
+
78
+ # The sender itself — a number in E.164, or an alphanumeric sender ID.
79
+ #
80
+ # Always present, and null until a sender exists. The key is on every delivery so
81
+ # a subscriber reads one shape rather than branching on whether the field arrived
82
+ # — the same choice template_id makes on the message payload.
83
+ #
84
+ # It can carry a value at any point in the lifecycle, not only once the market is
85
+ # live: a number ordered and not yet active at the carrier is already known during
86
+ # PROVISIONING, and an alphanumeric sender the customer chose themselves is known
87
+ # before anything is filed. It is null while the market is still waiting on a
88
+ # number, which for a US 10DLC registration is every event up to
89
+ # channel.activated.
90
+ sig { returns(T.nilable(String)) }
91
+ attr_accessor :sender_value
92
+
93
+ # Where the market stands: PENDING_REVIEW, ACTION_NEEDED, PROVISIONING, ACTIVE or
94
+ # INACTIVE. PENDING_REVIEW means a registry or a carrier holds it and the wait is
95
+ # theirs; ACTION_NEEDED means it is yours; PROVISIONING means the verdict is in
96
+ # and Sent is acquiring the sender; INACTIVE means it had a working sender and no
97
+ # longer does.
98
+ #
99
+ # Each event name is the transition into one of these, but the two are separate
100
+ # fields and may legitimately differ. A resubmission filed against a market whose
101
+ # sender is already live is channel.submitted carrying ACTIVE: a correction is
102
+ # with the registry and the sender keeps working. Read both.
103
+ sig { returns(T.nilable(String)) }
104
+ attr_reader :status
105
+
106
+ sig { params(status: String).void }
107
+ attr_writer :status
108
+
109
+ # When the transition happened, in UTC (yyyy-MM-ddTHH:mm:ssZ).
110
+ sig { returns(T.nilable(String)) }
111
+ attr_reader :updated_at
112
+
113
+ sig { params(updated_at: String).void }
114
+ attr_writer :updated_at
115
+
116
+ # Body of a channel event: where one of the customer's channels stands in
117
+ # provisioning and compliance. Delivered when a milestone moves — a registration
118
+ # filed, a verdict returned, a resubmission asked for, a sender gone live — so a
119
+ # customer's own onboarding UI does not have to poll GET /v3/channels.
120
+ #
121
+ # The subject is one item, never the account. A customer's "SMS channel" has no
122
+ # status; a market does. Country, NumberType and SenderValue name which one, so a
123
+ # customer terminating only to Kosovo never receives an event about US 10DLC.
124
+ #
125
+ # Status is the stable half of the contract. It is the same four-value set GET
126
+ # /v3/channels publishes, computed through the same code, so an event and a read
127
+ # of the same market cannot disagree. A subscriber that reads nothing but the
128
+ # status and the subject fields is a correct subscriber. The sub-type on the
129
+ # envelope names the specific milestone and is additive — that vocabulary comes
130
+ # from registries and carriers, which are parties Sent does not control.
131
+ #
132
+ # Status means provisioning and compliance are complete, not that a send will
133
+ # succeed right now. An account can be suspended, or a destination blocked by a
134
+ # routing rule, without either showing up here. Those are separate surfaces and
135
+ # deliberately not modelled on this payload.
136
+ sig do
137
+ params(
138
+ country: String,
139
+ account_id: String,
140
+ channel: String,
141
+ compliance:
142
+ T.nilable(Sentdm::ChannelEventPayload::Compliance::OrHash),
143
+ number_type: T.nilable(String),
144
+ reason: T.nilable(String),
145
+ sender_value: T.nilable(String),
146
+ status: String,
147
+ updated_at: String
148
+ ).returns(T.attached_class)
149
+ end
150
+ def self.new(
151
+ # The market's destination country as an ISO 3166-1 alpha-2 code, for example XK.
152
+ # Always present, and the property that identifies this payload among the
153
+ # delivered envelopes — see DeliveredWebhookEvents. Every event in this family
154
+ # reports one market, and a market has a country.
155
+ country:,
156
+ # The account whose market this is, named as on every other family. When an
157
+ # organization receives an event for one of its sender profiles this is the
158
+ # profile, so a reseller compares it with its own id and anything different is one
159
+ # of its profiles. Matches customer_id on GET /v3/channels and the sender
160
+ # profile's id. Together with channel, country, and number_type, it identifies the
161
+ # market.
162
+ account_id: nil,
163
+ # The channel this market belongs to: sms, whatsapp, or rcs. Never sent — that
164
+ # value belongs to message events, where it names the smart-routing brand rather
165
+ # than a channel that can be provisioned.
166
+ channel: nil,
167
+ # What a market has been given: the identity it registers under, its programme,
168
+ # and any documents attached.
169
+ #
170
+ # What it does not carry is what the market asks for. That is the subject of GET
171
+ # /v3/compliance/requirements, and it is the same answer for every caller — a
172
+ # description of what a compliance regime wants, not a record of one customer's
173
+ # progress through it. It was reported here as well for a while, which put the
174
+ # same array in six response shapes and left a caller deciding which of two
175
+ # sources to believe.
176
+ #
177
+ # Present on a list read for markets that register (carrying brand and campaign),
178
+ # but with documents absent — documents are not fetched for a list, because a
179
+ # catalog lookup and a document read per market would multiply across a page.
180
+ # Absent documents is distinct from an empty list: absent says they were not
181
+ # fetched; empty says the market has been given none. The parent object is null
182
+ # only when the market registers with nobody and compliance was not computed —
183
+ # nothing to show at all.
184
+ compliance: nil,
185
+ # The kind of sender the market uses, for example TEN_DLC, LOCAL, or ALPHANUMERIC.
186
+ # Omitted when the subject has no sender type of its own.
187
+ number_type: nil,
188
+ # Why the market reached this state, when a reason was given — a correction
189
+ # explained, or a campaign lapse. Free text, passed through from the registry or
190
+ # carrier that wrote it, so treat it as a message to show a human rather than a
191
+ # value to branch on.
192
+ reason: nil,
193
+ # The sender itself — a number in E.164, or an alphanumeric sender ID.
194
+ #
195
+ # Always present, and null until a sender exists. The key is on every delivery so
196
+ # a subscriber reads one shape rather than branching on whether the field arrived
197
+ # — the same choice template_id makes on the message payload.
198
+ #
199
+ # It can carry a value at any point in the lifecycle, not only once the market is
200
+ # live: a number ordered and not yet active at the carrier is already known during
201
+ # PROVISIONING, and an alphanumeric sender the customer chose themselves is known
202
+ # before anything is filed. It is null while the market is still waiting on a
203
+ # number, which for a US 10DLC registration is every event up to
204
+ # channel.activated.
205
+ sender_value: nil,
206
+ # Where the market stands: PENDING_REVIEW, ACTION_NEEDED, PROVISIONING, ACTIVE or
207
+ # INACTIVE. PENDING_REVIEW means a registry or a carrier holds it and the wait is
208
+ # theirs; ACTION_NEEDED means it is yours; PROVISIONING means the verdict is in
209
+ # and Sent is acquiring the sender; INACTIVE means it had a working sender and no
210
+ # longer does.
211
+ #
212
+ # Each event name is the transition into one of these, but the two are separate
213
+ # fields and may legitimately differ. A resubmission filed against a market whose
214
+ # sender is already live is channel.submitted carrying ACTIVE: a correction is
215
+ # with the registry and the sender keeps working. Read both.
216
+ status: nil,
217
+ # When the transition happened, in UTC (yyyy-MM-ddTHH:mm:ssZ).
218
+ updated_at: nil
219
+ )
220
+ end
221
+
222
+ sig do
223
+ override.returns(
224
+ {
225
+ country: String,
226
+ account_id: String,
227
+ channel: String,
228
+ compliance: T.nilable(Sentdm::ChannelEventPayload::Compliance),
229
+ number_type: T.nilable(String),
230
+ reason: T.nilable(String),
231
+ sender_value: T.nilable(String),
232
+ status: String,
233
+ updated_at: String
234
+ }
235
+ )
236
+ end
237
+ def to_hash
238
+ end
239
+
240
+ class Compliance < Sentdm::Internal::Type::BaseModel
241
+ OrHash =
242
+ T.type_alias do
243
+ T.any(
244
+ Sentdm::ChannelEventPayload::Compliance,
245
+ Sentdm::Internal::AnyHash
246
+ )
247
+ end
248
+
249
+ # The identity this market registers under, with inherit saying whose it is.
250
+ #
251
+ # Reported here rather than on the profile because it belongs to the registration
252
+ # this market files, and only one market files one. It was a top-level block for a
253
+ # while, which put a per-registration value beside a list of markets and left a
254
+ # caller to work out which market it belonged to.
255
+ #
256
+ # Absent for a market that registers with nobody — such a market asks for no
257
+ # identity, so there is none to report. Absent and null mean different things:
258
+ # absent says this market does not ask, null would say it asks and nothing was
259
+ # supplied.
260
+ #
261
+ # Untyped, like the request side, because its members are declared by the market's
262
+ # own schema rather than by a C# class. A typed pair here would be a second
263
+ # definition of what a market wants, free to drift from the one that validates.
264
+ sig { returns(T.nilable(T::Hash[Symbol, T.anything])) }
265
+ attr_accessor :brand
266
+
267
+ # The programme this market registers, with inherit saying whose it is.
268
+ #
269
+ # One, not a list. TcrCampaigns permits several and an account built on the admin
270
+ # side may hold them, but this surface offers one — which is what lets the
271
+ # market's PATCH be an upsert rather than a collection with an addressable create
272
+ # behind it. An account holding several is reported as its first and refused on
273
+ # write, rather than half-edited.
274
+ #
275
+ # Carries no id. Nothing addresses a campaign, and an undeclared key would be
276
+ # refused if the caller sent this object back — which it is meant to be able to
277
+ # do.
278
+ sig { returns(T.nilable(T::Hash[Symbol, T.anything])) }
279
+ attr_accessor :campaign
280
+
281
+ # What has been supplied for this market.
282
+ #
283
+ # Files, not values — the declared halves above carry the values. A document
284
+ # cannot be a JSON value, so it is sent as multipart on the channel call and
285
+ # reported here as a reference.
286
+ #
287
+ # Absent on a list read, which fetches identity but does not compute compliance
288
+ # documents per market. Absent and empty mean different things: absent says the
289
+ # documents were not fetched; empty says the market has been given none.
290
+ sig do
291
+ returns(
292
+ T.nilable(
293
+ T::Array[Sentdm::ChannelEventPayload::Compliance::Document]
294
+ )
295
+ )
296
+ end
297
+ attr_accessor :documents
298
+
299
+ # What a market has been given: the identity it registers under, its programme,
300
+ # and any documents attached.
301
+ #
302
+ # What it does not carry is what the market asks for. That is the subject of GET
303
+ # /v3/compliance/requirements, and it is the same answer for every caller — a
304
+ # description of what a compliance regime wants, not a record of one customer's
305
+ # progress through it. It was reported here as well for a while, which put the
306
+ # same array in six response shapes and left a caller deciding which of two
307
+ # sources to believe.
308
+ #
309
+ # Present on a list read for markets that register (carrying brand and campaign),
310
+ # but with documents absent — documents are not fetched for a list, because a
311
+ # catalog lookup and a document read per market would multiply across a page.
312
+ # Absent documents is distinct from an empty list: absent says they were not
313
+ # fetched; empty says the market has been given none. The parent object is null
314
+ # only when the market registers with nobody and compliance was not computed —
315
+ # nothing to show at all.
316
+ sig do
317
+ params(
318
+ brand: T.nilable(T::Hash[Symbol, T.anything]),
319
+ campaign: T.nilable(T::Hash[Symbol, T.anything]),
320
+ documents:
321
+ T.nilable(
322
+ T::Array[
323
+ Sentdm::ChannelEventPayload::Compliance::Document::OrHash
324
+ ]
325
+ )
326
+ ).returns(T.attached_class)
327
+ end
328
+ def self.new(
329
+ # The identity this market registers under, with inherit saying whose it is.
330
+ #
331
+ # Reported here rather than on the profile because it belongs to the registration
332
+ # this market files, and only one market files one. It was a top-level block for a
333
+ # while, which put a per-registration value beside a list of markets and left a
334
+ # caller to work out which market it belonged to.
335
+ #
336
+ # Absent for a market that registers with nobody — such a market asks for no
337
+ # identity, so there is none to report. Absent and null mean different things:
338
+ # absent says this market does not ask, null would say it asks and nothing was
339
+ # supplied.
340
+ #
341
+ # Untyped, like the request side, because its members are declared by the market's
342
+ # own schema rather than by a C# class. A typed pair here would be a second
343
+ # definition of what a market wants, free to drift from the one that validates.
344
+ brand: nil,
345
+ # The programme this market registers, with inherit saying whose it is.
346
+ #
347
+ # One, not a list. TcrCampaigns permits several and an account built on the admin
348
+ # side may hold them, but this surface offers one — which is what lets the
349
+ # market's PATCH be an upsert rather than a collection with an addressable create
350
+ # behind it. An account holding several is reported as its first and refused on
351
+ # write, rather than half-edited.
352
+ #
353
+ # Carries no id. Nothing addresses a campaign, and an undeclared key would be
354
+ # refused if the caller sent this object back — which it is meant to be able to
355
+ # do.
356
+ campaign: nil,
357
+ # What has been supplied for this market.
358
+ #
359
+ # Files, not values — the declared halves above carry the values. A document
360
+ # cannot be a JSON value, so it is sent as multipart on the channel call and
361
+ # reported here as a reference.
362
+ #
363
+ # Absent on a list read, which fetches identity but does not compute compliance
364
+ # documents per market. Absent and empty mean different things: absent says the
365
+ # documents were not fetched; empty says the market has been given none.
366
+ documents: nil
367
+ )
368
+ end
369
+
370
+ sig do
371
+ override.returns(
372
+ {
373
+ brand: T.nilable(T::Hash[Symbol, T.anything]),
374
+ campaign: T.nilable(T::Hash[Symbol, T.anything]),
375
+ documents:
376
+ T.nilable(
377
+ T::Array[Sentdm::ChannelEventPayload::Compliance::Document]
378
+ )
379
+ }
380
+ )
381
+ end
382
+ def to_hash
383
+ end
384
+
385
+ class Document < Sentdm::Internal::Type::BaseModel
386
+ OrHash =
387
+ T.type_alias do
388
+ T.any(
389
+ Sentdm::ChannelEventPayload::Compliance::Document,
390
+ Sentdm::Internal::AnyHash
391
+ )
392
+ end
393
+
394
+ # Identifier of the upload, for fetching it back through the documents endpoints.
395
+ sig { returns(T.nilable(String)) }
396
+ attr_accessor :document_id
397
+
398
+ sig { returns(T.nilable(String)) }
399
+ attr_accessor :file_name
400
+
401
+ # The catalog's name for this document, matching the requirement it satisfies.
402
+ sig { returns(T.nilable(String)) }
403
+ attr_reader :key
404
+
405
+ sig { params(key: String).void }
406
+ attr_writer :key
407
+
408
+ # A document a market asked for and has been given.
409
+ sig do
410
+ params(
411
+ document_id: T.nilable(String),
412
+ file_name: T.nilable(String),
413
+ key: String
414
+ ).returns(T.attached_class)
415
+ end
416
+ def self.new(
417
+ # Identifier of the upload, for fetching it back through the documents endpoints.
418
+ document_id: nil,
419
+ file_name: nil,
420
+ # The catalog's name for this document, matching the requirement it satisfies.
421
+ key: nil
422
+ )
423
+ end
424
+
425
+ sig do
426
+ override.returns(
427
+ {
428
+ document_id: T.nilable(String),
429
+ file_name: T.nilable(String),
430
+ key: String
431
+ }
432
+ )
433
+ end
434
+ def to_hash
435
+ end
436
+ end
437
+ end
438
+ end
439
+ end
440
+ end
@@ -0,0 +1,126 @@
1
+ # typed: strong
2
+
3
+ module Sentdm
4
+ module Models
5
+ class ContactEvent < Sentdm::Internal::Type::BaseModel
6
+ OrHash =
7
+ T.type_alias { T.any(Sentdm::ContactEvent, Sentdm::Internal::AnyHash) }
8
+
9
+ # The specific event within the family, for example message.delivered,
10
+ # message.received or contact.opt_out. Absent on events that have no subtype, so
11
+ # treat it as optional.
12
+ sig { returns(T.nilable(String)) }
13
+ attr_accessor :event
14
+
15
+ # The event family, for example message, templates or contact. Route on this
16
+ # first, then on event for the specific change.
17
+ sig { returns(T.nilable(String)) }
18
+ attr_reader :field
19
+
20
+ sig { params(field: String).void }
21
+ attr_writer :field
22
+
23
+ # Body of a contact.opt_in, contact.opt_out, contact.help or
24
+ # contact.custom_keyword event. Delivered when a contact signals a consent change,
25
+ # asks for help, or sends one of your own auto-reply keywords.
26
+ #
27
+ # These events state the signal outright, so you do not have to recognise keywords
28
+ # in the text of a message.received event. They also cover cases that produce no
29
+ # inbound message at all, such as a network handling an opt-out on your behalf.
30
+ #
31
+ # Two of the four change consent and two do not: contact.help and
32
+ # contact.custom_keyword report the state the contact already had. Read opt_out
33
+ # for the state and the envelope's event for what happened, rather than inferring
34
+ # one from the other.
35
+ #
36
+ # Fields are ordered identity → resulting state → provenance → join keys. The two
37
+ # parties are from and to. Note that the message family has not moved to those
38
+ # names yet — message.received still calls the same two parties inbound_number and
39
+ # outbound_number. Nothing here restates the envelope: which signal occurred is
40
+ # the envelope's event, and when it was emitted is its timestamp. Retries carry
41
+ # the same X-Webhook-Event-ID header, which is what to deduplicate on.
42
+ sig { returns(T.nilable(Sentdm::ContactEventPayload)) }
43
+ attr_reader :payload
44
+
45
+ sig do
46
+ params(payload: T.nilable(Sentdm::ContactEventPayload::OrHash)).void
47
+ end
48
+ attr_writer :payload
49
+
50
+ # The event-specific body.
51
+ sig { returns(T.nilable(String)) }
52
+ attr_accessor :request_id
53
+
54
+ # When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
55
+ # time, not the time the underlying change happened. Use the timestamp inside the
56
+ # payload for the latter.
57
+ sig { returns(T.nilable(String)) }
58
+ attr_reader :timestamp
59
+
60
+ sig { params(timestamp: String).void }
61
+ attr_writer :timestamp
62
+
63
+ # The envelope Sent POSTs to a subscribed webhook endpoint. Every event shares
64
+ # this shape and varies only in Payload.
65
+ sig do
66
+ params(
67
+ event: T.nilable(String),
68
+ field: String,
69
+ payload: T.nilable(Sentdm::ContactEventPayload::OrHash),
70
+ request_id: T.nilable(String),
71
+ timestamp: String
72
+ ).returns(T.attached_class)
73
+ end
74
+ def self.new(
75
+ # The specific event within the family, for example message.delivered,
76
+ # message.received or contact.opt_out. Absent on events that have no subtype, so
77
+ # treat it as optional.
78
+ event: nil,
79
+ # The event family, for example message, templates or contact. Route on this
80
+ # first, then on event for the specific change.
81
+ field: nil,
82
+ # Body of a contact.opt_in, contact.opt_out, contact.help or
83
+ # contact.custom_keyword event. Delivered when a contact signals a consent change,
84
+ # asks for help, or sends one of your own auto-reply keywords.
85
+ #
86
+ # These events state the signal outright, so you do not have to recognise keywords
87
+ # in the text of a message.received event. They also cover cases that produce no
88
+ # inbound message at all, such as a network handling an opt-out on your behalf.
89
+ #
90
+ # Two of the four change consent and two do not: contact.help and
91
+ # contact.custom_keyword report the state the contact already had. Read opt_out
92
+ # for the state and the envelope's event for what happened, rather than inferring
93
+ # one from the other.
94
+ #
95
+ # Fields are ordered identity → resulting state → provenance → join keys. The two
96
+ # parties are from and to. Note that the message family has not moved to those
97
+ # names yet — message.received still calls the same two parties inbound_number and
98
+ # outbound_number. Nothing here restates the envelope: which signal occurred is
99
+ # the envelope's event, and when it was emitted is its timestamp. Retries carry
100
+ # the same X-Webhook-Event-ID header, which is what to deduplicate on.
101
+ payload: nil,
102
+ # The event-specific body.
103
+ request_id: nil,
104
+ # When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
105
+ # time, not the time the underlying change happened. Use the timestamp inside the
106
+ # payload for the latter.
107
+ timestamp: nil
108
+ )
109
+ end
110
+
111
+ sig do
112
+ override.returns(
113
+ {
114
+ event: T.nilable(String),
115
+ field: String,
116
+ payload: T.nilable(Sentdm::ContactEventPayload),
117
+ request_id: T.nilable(String),
118
+ timestamp: String
119
+ }
120
+ )
121
+ end
122
+ def to_hash
123
+ end
124
+ end
125
+ end
126
+ end