sentdm 0.32.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 (70) 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_payload.rb +154 -7
  6. data/lib/sentdm/models/contact_event.rb +15 -7
  7. data/lib/sentdm/models/contact_event_payload.rb +77 -16
  8. data/lib/sentdm/models/conversation_messages_list.rb +214 -6
  9. data/lib/sentdm/models/inbound_message_event_payload.rb +70 -3
  10. data/lib/sentdm/models/me_retrieve_response.rb +21 -1
  11. data/lib/sentdm/models/message_event_payload.rb +42 -1
  12. data/lib/sentdm/models/message_retrieve_activities_response.rb +42 -5
  13. data/lib/sentdm/models/message_retrieve_status_response.rb +222 -8
  14. data/lib/sentdm/models/message_send_params.rb +67 -3
  15. data/lib/sentdm/models/message_send_response.rb +13 -4
  16. data/lib/sentdm/models/template_body.rb +82 -1
  17. data/lib/sentdm/models/template_create_params.rb +11 -1
  18. data/lib/sentdm/models/template_header.rb +91 -3
  19. data/lib/sentdm/models/template_variable.rb +15 -1
  20. data/lib/sentdm/models/webhook_create_params.rb +31 -1
  21. data/lib/sentdm/models/webhook_list_events_response.rb +497 -9
  22. data/lib/sentdm/models/webhook_update_params.rb +31 -1
  23. data/lib/sentdm/resources/me.rb +5 -0
  24. data/lib/sentdm/resources/messages.rb +33 -5
  25. data/lib/sentdm/resources/templates.rb +3 -1
  26. data/lib/sentdm/resources/webhooks.rb +9 -3
  27. data/lib/sentdm/version.rb +1 -1
  28. data/lib/sentdm.rb +1 -1
  29. data/rbi/sentdm/client.rbi +6 -0
  30. data/rbi/sentdm/models/channel_event_payload.rbi +273 -10
  31. data/rbi/sentdm/models/contact_event.rbi +28 -12
  32. data/rbi/sentdm/models/contact_event_payload.rbi +118 -28
  33. data/rbi/sentdm/models/conversation_messages_list.rbi +308 -10
  34. data/rbi/sentdm/models/inbound_message_event_payload.rbi +110 -2
  35. data/rbi/sentdm/models/me_retrieve_response.rbi +35 -0
  36. data/rbi/sentdm/models/message_event_payload.rbi +50 -0
  37. data/rbi/sentdm/models/message_retrieve_activities_response.rbi +49 -5
  38. data/rbi/sentdm/models/message_retrieve_status_response.rbi +320 -12
  39. data/rbi/sentdm/models/message_send_params.rbi +100 -2
  40. data/rbi/sentdm/models/message_send_response.rbi +15 -5
  41. data/rbi/sentdm/models/template_body.rbi +133 -0
  42. data/rbi/sentdm/models/template_create_params.rbi +15 -0
  43. data/rbi/sentdm/models/template_header.rbi +143 -2
  44. data/rbi/sentdm/models/template_variable.rbi +10 -0
  45. data/rbi/sentdm/models/webhook_create_params.rbi +61 -0
  46. data/rbi/sentdm/models/webhook_list_events_response.rbi +747 -12
  47. data/rbi/sentdm/models/webhook_update_params.rbi +61 -0
  48. data/rbi/sentdm/resources/me.rbi +5 -0
  49. data/rbi/sentdm/resources/messages.rbi +58 -3
  50. data/rbi/sentdm/resources/templates.rbi +6 -0
  51. data/rbi/sentdm/resources/webhooks.rbi +15 -1
  52. data/sig/sentdm/models/channel_event_payload.rbs +62 -0
  53. data/sig/sentdm/models/contact_event_payload.rbs +24 -9
  54. data/sig/sentdm/models/conversation_messages_list.rbs +99 -6
  55. data/sig/sentdm/models/inbound_message_event_payload.rbs +37 -0
  56. data/sig/sentdm/models/me_retrieve_response.rbs +7 -0
  57. data/sig/sentdm/models/message_event_payload.rbs +20 -0
  58. data/sig/sentdm/models/message_retrieve_activities_response.rbs +15 -0
  59. data/sig/sentdm/models/message_retrieve_status_response.rbs +99 -6
  60. data/sig/sentdm/models/message_send_params.rbs +15 -0
  61. data/sig/sentdm/models/template_body.rbs +44 -0
  62. data/sig/sentdm/models/template_create_params.rbs +7 -0
  63. data/sig/sentdm/models/template_header.rbs +44 -0
  64. data/sig/sentdm/models/webhook_create_params.rbs +29 -0
  65. data/sig/sentdm/models/webhook_list_events_response.rbs +252 -0
  66. data/sig/sentdm/models/webhook_update_params.rbs +29 -0
  67. data/sig/sentdm/resources/messages.rbs +3 -0
  68. data/sig/sentdm/resources/templates.rbs +1 -0
  69. data/sig/sentdm/resources/webhooks.rbs +2 -0
  70. metadata +2 -2
@@ -13,12 +13,20 @@ 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
  # Some parameter documentations has been truncated, see
18
24
  # {Sentdm::Models::MessageRetrieveActivitiesParams} for more details.
19
25
  #
20
26
  # Retrieves the activity log for a specific message. Activities track the message
21
- # lifecycle including acceptance, processing, sending, delivery, and any errors.
27
+ # lifecycle including acceptance, processing, sending, delivery, and any errors. A
28
+ # SCHEDULED entry carries scheduled_at, the release instant in UTC as it stood at
29
+ # that moment. Other entries have no scheduled_at key.
22
30
  #
23
31
  # @overload retrieve_activities(id, x_profile_id: nil, request_options: {})
24
32
  #
@@ -46,7 +54,11 @@ module Sentdm
46
54
  # {Sentdm::Models::MessageRetrieveStatusParams} for more details.
47
55
  #
48
56
  # Retrieves the current status and details of a message by ID. Includes delivery
49
- # status, timestamps, and error information if applicable.
57
+ # status, timestamps, and error information if applicable. A message that is or
58
+ # was held for a later time (a send you scheduled with scheduled_at, or a
59
+ # quiet-hours hold) is returned as a ScheduledMessageResponse: the same fields
60
+ # plus scheduled_at, the release instant in UTC. A message sent immediately has no
61
+ # scheduled_at key.
50
62
  #
51
63
  # @overload retrieve_status(id, x_profile_id: nil, request_options: {})
52
64
  #
@@ -82,14 +94,30 @@ module Sentdm
82
94
  # insufficient balance, a template not approved for sending, or free-form content
83
95
  # with no open conversation with the contact. The send is accepted with 202 and
84
96
  # the affected messages are reported as BLOCKED on GET /messages/{id} and the
85
- # message.blocked webhook.
86
- #
87
- # @overload send_(channel: nil, sandbox: nil, template: nil, text: nil, to: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
97
+ # message.blocked webhook. To send later, set scheduled_at (ISO-8601 with an
98
+ # explicit UTC offset; a value without one is rejected) between 1 minute and 30
99
+ # days ahead: the response is a ScheduledSendMessageResponse (the same fields plus
100
+ # scheduled_at; status is still QUEUED), each message then moves to SCHEDULED, is
101
+ # held and released at that time (within a few minutes), and a message.scheduled
102
+ # webhook fires once it is held. Balance and template approval are evaluated at
103
+ # release, not at acceptance. Quiet hours are not checked when the request is
104
+ # accepted: if the time falls inside a legally protected quiet-hours window for a
105
+ # recipient, that message is moved to the next allowed time at release and a
106
+ # second message.scheduled webhook reports the new scheduled_at. An account may
107
+ # hold at most 1,000,000 scheduled messages at once (429 LIMIT_001).
108
+ #
109
+ # @overload send_(channel: nil, media_urls: nil, sandbox: nil, scheduled_at: nil, subject: nil, template: nil, text: nil, to: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
88
110
  #
89
111
  # @param channel [Array<String>, nil] Body param: Channels to broadcast on, e.g. ["whatsapp", "sms"].
90
112
  #
113
+ # @param media_urls [Array<String>, nil] Body param: Attachments for this send, as publicly fetchable https URLs. Used by
114
+ #
91
115
  # @param sandbox [Boolean] Body param: Sandbox flag - when true, the operation is simulated without side ef
92
116
  #
117
+ # @param scheduled_at [Time, nil] Body param: Optional future send time as an ISO-8601 timestamp with an explicit
118
+ #
119
+ # @param subject [String, nil] Body param: Subject line for this send, overriding the template's. MMS only; ign
120
+ #
93
121
  # @param template [Sentdm::Models::MessageSendParams::Template, nil] Body param: SDK-style template reference: resolve by ID or by name, with optiona
94
122
  #
95
123
  # @param text [String, nil] Body param: Plain-text (free-form) message body. Provide either Template or this
@@ -19,7 +19,9 @@ module Sentdm
19
19
  # from the template's content and can be changed afterwards with
20
20
  # `PUT /v3/templates/{id}`.
21
21
  #
22
- # @overload create(category: nil, creation_source: nil, definition: nil, language: nil, sandbox: nil, submit_for_review: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
22
+ # @overload create(auto_create_for_sp: nil, category: nil, creation_source: nil, definition: nil, language: nil, sandbox: nil, submit_for_review: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
23
+ #
24
+ # @param auto_create_for_sp [Boolean] Body param: Create this template automatically on every sender profile of the or
23
25
  #
24
26
  # @param category [String, nil] Body param: Template category: MARKETING, UTILITY, AUTHENTICATION (optional, aut
25
27
  #
@@ -19,7 +19,7 @@ module Sentdm
19
19
  #
20
20
  # Creates a new webhook endpoint for the authenticated customer.
21
21
  #
22
- # @overload create(display_name: nil, endpoint_url: nil, event_filters: nil, event_types: nil, retry_count: nil, sandbox: nil, timeout_seconds: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
22
+ # @overload create(display_name: nil, endpoint_url: nil, event_filters: nil, event_types: nil, retry_count: nil, sandbox: nil, sender_profile: nil, timeout_seconds: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
23
23
  #
24
24
  # @param display_name [String] Body param
25
25
  #
@@ -33,6 +33,8 @@ module Sentdm
33
33
  #
34
34
  # @param sandbox [Boolean] Body param: Sandbox flag - when true, the operation is simulated without side ef
35
35
  #
36
+ # @param sender_profile [Sentdm::Models::WebhookCreateParams::SenderProfile, nil] Body param: Request-only: the events an organization webhook's sender profile cl
37
+ #
36
38
  # @param timeout_seconds [Integer] Body param
37
39
  #
38
40
  # @param idempotency_key [String] Header param: Unique key to ensure idempotent request processing. Must be 1-255
@@ -89,7 +91,7 @@ module Sentdm
89
91
  #
90
92
  # Updates an existing webhook for the authenticated customer.
91
93
  #
92
- # @overload update(id, display_name: nil, endpoint_url: nil, event_filters: nil, event_types: nil, retry_count: nil, sandbox: nil, timeout_seconds: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
94
+ # @overload update(id, display_name: nil, endpoint_url: nil, event_filters: nil, event_types: nil, retry_count: nil, sandbox: nil, sender_profile: nil, timeout_seconds: nil, idempotency_key: nil, x_profile_id: nil, request_options: {})
93
95
  #
94
96
  # @param id [String] Path param
95
97
  #
@@ -105,6 +107,8 @@ module Sentdm
105
107
  #
106
108
  # @param sandbox [Boolean] Body param: Sandbox flag - when true, the operation is simulated without side ef
107
109
  #
110
+ # @param sender_profile [Sentdm::Models::WebhookUpdateParams::SenderProfile, nil] Body param: Request-only: the events an organization webhook's sender profile cl
111
+ #
108
112
  # @param timeout_seconds [Integer] Body param
109
113
  #
110
114
  # @param idempotency_key [String] Header param: Unique key to ensure idempotent request processing. Must be 1-255
@@ -221,7 +225,9 @@ module Sentdm
221
225
  # Some parameter documentations has been truncated, see
222
226
  # {Sentdm::Models::WebhookListEventsParams} for more details.
223
227
  #
224
- # Retrieves a paginated list of delivery events for the specified webhook.
228
+ # Retrieves a paginated list of delivery events for the specified webhook. If the
229
+ # webhook is cloned onto your sender profiles, the list includes what those clones
230
+ # received; read payload.account_id to tell whose event it is.
225
231
  #
226
232
  # @overload list_events(id, page: nil, page_size: nil, search: nil, x_profile_id: nil, request_options: {})
227
233
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sentdm
4
- VERSION = "0.32.0"
4
+ VERSION = "0.34.0"
5
5
  end
data/lib/sentdm.rb CHANGED
@@ -58,6 +58,7 @@ require_relative "sentdm/internal/templates_page"
58
58
  require_relative "sentdm/internal/webhook_events_page"
59
59
  require_relative "sentdm/internal/webhooks_page"
60
60
  require_relative "sentdm/models/mutation_request"
61
+ require_relative "sentdm/models/template_body_content"
61
62
  require_relative "sentdm/models/api_meta"
62
63
  require_relative "sentdm/models/api_response_of_contact"
63
64
  require_relative "sentdm/models/api_response_of_contact_message_summary"
@@ -130,7 +131,6 @@ require_relative "sentdm/models/tcr_brand_relationship"
130
131
  require_relative "sentdm/models/tcr_vertical"
131
132
  require_relative "sentdm/models/template"
132
133
  require_relative "sentdm/models/template_body"
133
- require_relative "sentdm/models/template_body_content"
134
134
  require_relative "sentdm/models/template_button"
135
135
  require_relative "sentdm/models/template_button_props"
136
136
  require_relative "sentdm/models/template_create_params"
@@ -78,6 +78,12 @@ module Sentdm
78
78
  # **A message needs a sender.** What you can send, where, and at what cost is
79
79
  # decided by the markets under **Channels** — so a recipient in a country you hold
80
80
  # no sender for is refused here rather than queued.
81
+ #
82
+ # **A message can be resent on its id.** `POST /v3/messages/{id}/resend` puts a
83
+ # finished message — typically one BLOCKED for insufficient balance — back through
84
+ # the send pipeline. It is a new attempt, not a free retry: every policy runs
85
+ # again, the message is billed again, and its status webhooks fire again. A
86
+ # FILTERED message is never resendable.
81
87
  sig { returns(Sentdm::Resources::Messages) }
82
88
  attr_reader :messages
83
89
 
@@ -18,7 +18,9 @@ module Sentdm
18
18
  # The account whose market this is, named as on every other family. When an
19
19
  # organization receives an event for one of its sender profiles this is the
20
20
  # profile, so a reseller compares it with its own id and anything different is one
21
- # of its profiles.
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.
22
24
  sig { returns(T.nilable(String)) }
23
25
  attr_reader :account_id
24
26
 
@@ -34,18 +36,51 @@ module Sentdm
34
36
  sig { params(channel: String).void }
35
37
  attr_writer :channel
36
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
+
37
66
  # The kind of sender the market uses, for example TEN_DLC, LOCAL, or ALPHANUMERIC.
38
67
  # Omitted when the subject has no sender type of its own.
39
68
  sig { returns(T.nilable(String)) }
40
69
  attr_accessor :number_type
41
70
 
42
- # Why the market reached this state, when a reason was given — a correction
43
- # explained, or a campaign lapse. Free text, passed through from the registry or
44
- # carrier that wrote it, so treat it as a message to show a human rather than a
45
- # value to branch on.
71
+ # Why the market reached this state, as a sentence to show a person: the specific
72
+ # explanation when one was given (a correction explained, a campaign lapse),
73
+ # otherwise what reason_code means for this market. Not a value to branch on.
46
74
  sig { returns(T.nilable(String)) }
47
75
  attr_accessor :reason
48
76
 
77
+ # Why the market is not ACTIVE, as a stable code: an ErrorCodes CHANNEL_xxx value
78
+ # such as CHANNEL_001 (something you owe) or CHANNEL_002 (a correction was
79
+ # requested). The same code the channels resource reports for the market. Switch
80
+ # on this rather than on reason. Omitted while ACTIVE.
81
+ sig { returns(T.nilable(String)) }
82
+ attr_accessor :reason_code
83
+
49
84
  # The sender itself — a number in E.164, or an alphanumeric sender ID.
50
85
  #
51
86
  # Always present, and null until a sender exists. The key is on every delivery so
@@ -109,8 +144,11 @@ module Sentdm
109
144
  country: String,
110
145
  account_id: String,
111
146
  channel: String,
147
+ compliance:
148
+ T.nilable(Sentdm::ChannelEventPayload::Compliance::OrHash),
112
149
  number_type: T.nilable(String),
113
150
  reason: T.nilable(String),
151
+ reason_code: T.nilable(String),
114
152
  sender_value: T.nilable(String),
115
153
  status: String,
116
154
  updated_at: String
@@ -125,20 +163,44 @@ module Sentdm
125
163
  # The account whose market this is, named as on every other family. When an
126
164
  # organization receives an event for one of its sender profiles this is the
127
165
  # profile, so a reseller compares it with its own id and anything different is one
128
- # of its profiles.
166
+ # of its profiles. Matches customer_id on GET /v3/channels and the sender
167
+ # profile's id. Together with channel, country, and number_type, it identifies the
168
+ # market.
129
169
  account_id: nil,
130
170
  # The channel this market belongs to: sms, whatsapp, or rcs. Never sent — that
131
171
  # value belongs to message events, where it names the smart-routing brand rather
132
172
  # than a channel that can be provisioned.
133
173
  channel: nil,
174
+ # What a market has been given: the identity it registers under, its programme,
175
+ # and any documents attached.
176
+ #
177
+ # What it does not carry is what the market asks for. That is the subject of GET
178
+ # /v3/compliance/requirements, and it is the same answer for every caller — a
179
+ # description of what a compliance regime wants, not a record of one customer's
180
+ # progress through it. It was reported here as well for a while, which put the
181
+ # same array in six response shapes and left a caller deciding which of two
182
+ # sources to believe.
183
+ #
184
+ # Present on a list read for markets that register (carrying brand and campaign),
185
+ # but with documents absent — documents are not fetched for a list, because a
186
+ # catalog lookup and a document read per market would multiply across a page.
187
+ # Absent documents is distinct from an empty list: absent says they were not
188
+ # fetched; empty says the market has been given none. The parent object is null
189
+ # only when the market registers with nobody and compliance was not computed —
190
+ # nothing to show at all.
191
+ compliance: nil,
134
192
  # The kind of sender the market uses, for example TEN_DLC, LOCAL, or ALPHANUMERIC.
135
193
  # Omitted when the subject has no sender type of its own.
136
194
  number_type: nil,
137
- # Why the market reached this state, when a reason was given — a correction
138
- # explained, or a campaign lapse. Free text, passed through from the registry or
139
- # carrier that wrote it, so treat it as a message to show a human rather than a
140
- # value to branch on.
195
+ # Why the market reached this state, as a sentence to show a person: the specific
196
+ # explanation when one was given (a correction explained, a campaign lapse),
197
+ # otherwise what reason_code means for this market. Not a value to branch on.
141
198
  reason: nil,
199
+ # Why the market is not ACTIVE, as a stable code: an ErrorCodes CHANNEL_xxx value
200
+ # such as CHANNEL_001 (something you owe) or CHANNEL_002 (a correction was
201
+ # requested). The same code the channels resource reports for the market. Switch
202
+ # on this rather than on reason. Omitted while ACTIVE.
203
+ reason_code: nil,
142
204
  # The sender itself — a number in E.164, or an alphanumeric sender ID.
143
205
  #
144
206
  # Always present, and null until a sender exists. The key is on every delivery so
@@ -174,8 +236,10 @@ module Sentdm
174
236
  country: String,
175
237
  account_id: String,
176
238
  channel: String,
239
+ compliance: T.nilable(Sentdm::ChannelEventPayload::Compliance),
177
240
  number_type: T.nilable(String),
178
241
  reason: T.nilable(String),
242
+ reason_code: T.nilable(String),
179
243
  sender_value: T.nilable(String),
180
244
  status: String,
181
245
  updated_at: String
@@ -184,6 +248,205 @@ module Sentdm
184
248
  end
185
249
  def to_hash
186
250
  end
251
+
252
+ class Compliance < Sentdm::Internal::Type::BaseModel
253
+ OrHash =
254
+ T.type_alias do
255
+ T.any(
256
+ Sentdm::ChannelEventPayload::Compliance,
257
+ Sentdm::Internal::AnyHash
258
+ )
259
+ end
260
+
261
+ # The identity this market registers under, with inherit saying whose it is.
262
+ #
263
+ # Reported here rather than on the profile because it belongs to the registration
264
+ # this market files, and only one market files one. It was a top-level block for a
265
+ # while, which put a per-registration value beside a list of markets and left a
266
+ # caller to work out which market it belonged to.
267
+ #
268
+ # Absent for a market that registers with nobody — such a market asks for no
269
+ # identity, so there is none to report. Absent and null mean different things:
270
+ # absent says this market does not ask, null would say it asks and nothing was
271
+ # supplied.
272
+ #
273
+ # Untyped, like the request side, because its members are declared by the market's
274
+ # own schema rather than by a C# class. A typed pair here would be a second
275
+ # definition of what a market wants, free to drift from the one that validates.
276
+ sig { returns(T.nilable(T::Hash[Symbol, T.anything])) }
277
+ attr_accessor :brand
278
+
279
+ # The programme this market registers, with inherit saying whose it is.
280
+ #
281
+ # One, not a list. TcrCampaigns permits several and an account built on the admin
282
+ # side may hold them, but this surface offers one — which is what lets the
283
+ # market's PATCH be an upsert rather than a collection with an addressable create
284
+ # behind it. An account holding several is reported as its first and refused on
285
+ # write, rather than half-edited.
286
+ #
287
+ # Carries no id. Nothing addresses a campaign, and an undeclared key would be
288
+ # refused if the caller sent this object back — which it is meant to be able to
289
+ # do.
290
+ sig { returns(T.nilable(T::Hash[Symbol, T.anything])) }
291
+ attr_accessor :campaign
292
+
293
+ # What has been supplied for this market.
294
+ #
295
+ # Files, not values — the declared halves above carry the values. A document
296
+ # cannot be a JSON value, so it is sent as multipart on the channel call and
297
+ # reported here as a reference.
298
+ #
299
+ # Absent on a list read, which fetches identity but does not compute compliance
300
+ # documents per market. Absent and empty mean different things: absent says the
301
+ # documents were not fetched; empty says the market has been given none.
302
+ sig do
303
+ returns(
304
+ T.nilable(
305
+ T::Array[Sentdm::ChannelEventPayload::Compliance::Document]
306
+ )
307
+ )
308
+ end
309
+ attr_accessor :documents
310
+
311
+ # What a market has been given: the identity it registers under, its programme,
312
+ # and any documents attached.
313
+ #
314
+ # What it does not carry is what the market asks for. That is the subject of GET
315
+ # /v3/compliance/requirements, and it is the same answer for every caller — a
316
+ # description of what a compliance regime wants, not a record of one customer's
317
+ # progress through it. It was reported here as well for a while, which put the
318
+ # same array in six response shapes and left a caller deciding which of two
319
+ # sources to believe.
320
+ #
321
+ # Present on a list read for markets that register (carrying brand and campaign),
322
+ # but with documents absent — documents are not fetched for a list, because a
323
+ # catalog lookup and a document read per market would multiply across a page.
324
+ # Absent documents is distinct from an empty list: absent says they were not
325
+ # fetched; empty says the market has been given none. The parent object is null
326
+ # only when the market registers with nobody and compliance was not computed —
327
+ # nothing to show at all.
328
+ sig do
329
+ params(
330
+ brand: T.nilable(T::Hash[Symbol, T.anything]),
331
+ campaign: T.nilable(T::Hash[Symbol, T.anything]),
332
+ documents:
333
+ T.nilable(
334
+ T::Array[
335
+ Sentdm::ChannelEventPayload::Compliance::Document::OrHash
336
+ ]
337
+ )
338
+ ).returns(T.attached_class)
339
+ end
340
+ def self.new(
341
+ # The identity this market registers under, with inherit saying whose it is.
342
+ #
343
+ # Reported here rather than on the profile because it belongs to the registration
344
+ # this market files, and only one market files one. It was a top-level block for a
345
+ # while, which put a per-registration value beside a list of markets and left a
346
+ # caller to work out which market it belonged to.
347
+ #
348
+ # Absent for a market that registers with nobody — such a market asks for no
349
+ # identity, so there is none to report. Absent and null mean different things:
350
+ # absent says this market does not ask, null would say it asks and nothing was
351
+ # supplied.
352
+ #
353
+ # Untyped, like the request side, because its members are declared by the market's
354
+ # own schema rather than by a C# class. A typed pair here would be a second
355
+ # definition of what a market wants, free to drift from the one that validates.
356
+ brand: nil,
357
+ # The programme this market registers, with inherit saying whose it is.
358
+ #
359
+ # One, not a list. TcrCampaigns permits several and an account built on the admin
360
+ # side may hold them, but this surface offers one — which is what lets the
361
+ # market's PATCH be an upsert rather than a collection with an addressable create
362
+ # behind it. An account holding several is reported as its first and refused on
363
+ # write, rather than half-edited.
364
+ #
365
+ # Carries no id. Nothing addresses a campaign, and an undeclared key would be
366
+ # refused if the caller sent this object back — which it is meant to be able to
367
+ # do.
368
+ campaign: nil,
369
+ # What has been supplied for this market.
370
+ #
371
+ # Files, not values — the declared halves above carry the values. A document
372
+ # cannot be a JSON value, so it is sent as multipart on the channel call and
373
+ # reported here as a reference.
374
+ #
375
+ # Absent on a list read, which fetches identity but does not compute compliance
376
+ # documents per market. Absent and empty mean different things: absent says the
377
+ # documents were not fetched; empty says the market has been given none.
378
+ documents: nil
379
+ )
380
+ end
381
+
382
+ sig do
383
+ override.returns(
384
+ {
385
+ brand: T.nilable(T::Hash[Symbol, T.anything]),
386
+ campaign: T.nilable(T::Hash[Symbol, T.anything]),
387
+ documents:
388
+ T.nilable(
389
+ T::Array[Sentdm::ChannelEventPayload::Compliance::Document]
390
+ )
391
+ }
392
+ )
393
+ end
394
+ def to_hash
395
+ end
396
+
397
+ class Document < Sentdm::Internal::Type::BaseModel
398
+ OrHash =
399
+ T.type_alias do
400
+ T.any(
401
+ Sentdm::ChannelEventPayload::Compliance::Document,
402
+ Sentdm::Internal::AnyHash
403
+ )
404
+ end
405
+
406
+ # Identifier of the upload, for fetching it back through the documents endpoints.
407
+ sig { returns(T.nilable(String)) }
408
+ attr_accessor :document_id
409
+
410
+ sig { returns(T.nilable(String)) }
411
+ attr_accessor :file_name
412
+
413
+ # The catalog's name for this document, matching the requirement it satisfies.
414
+ sig { returns(T.nilable(String)) }
415
+ attr_reader :key
416
+
417
+ sig { params(key: String).void }
418
+ attr_writer :key
419
+
420
+ # A document a market asked for and has been given.
421
+ sig do
422
+ params(
423
+ document_id: T.nilable(String),
424
+ file_name: T.nilable(String),
425
+ key: String
426
+ ).returns(T.attached_class)
427
+ end
428
+ def self.new(
429
+ # Identifier of the upload, for fetching it back through the documents endpoints.
430
+ document_id: nil,
431
+ file_name: nil,
432
+ # The catalog's name for this document, matching the requirement it satisfies.
433
+ key: nil
434
+ )
435
+ end
436
+
437
+ sig do
438
+ override.returns(
439
+ {
440
+ document_id: T.nilable(String),
441
+ file_name: T.nilable(String),
442
+ key: String
443
+ }
444
+ )
445
+ end
446
+ def to_hash
447
+ end
448
+ end
449
+ end
187
450
  end
188
451
  end
189
452
  end
@@ -20,17 +20,25 @@ module Sentdm
20
20
  sig { params(field: String).void }
21
21
  attr_writer :field
22
22
 
23
- # Body of a contact.opt_in, contact.opt_out or contact.help event. Delivered when
24
- # a contact signals a consent change or asks for help.
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.
25
26
  #
26
27
  # These events state the signal outright, so you do not have to recognise keywords
27
28
  # in the text of a message.received event. They also cover cases that produce no
28
29
  # inbound message at all, such as a network handling an opt-out on your behalf.
29
30
  #
30
- # Fields are ordered identity → resulting state → provenance → join key. Nothing
31
- # here restates the envelope: which of the three signals occurred is the
32
- # envelope's event, and when it was emitted is its timestamp. Retries carry the
33
- # same X-Webhook-Event-ID header, which is what to deduplicate on.
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.
34
42
  sig { returns(T.nilable(Sentdm::ContactEventPayload)) }
35
43
  attr_reader :payload
36
44
 
@@ -71,17 +79,25 @@ module Sentdm
71
79
  # The event family, for example message, templates or contact. Route on this
72
80
  # first, then on event for the specific change.
73
81
  field: nil,
74
- # Body of a contact.opt_in, contact.opt_out or contact.help event. Delivered when
75
- # a contact signals a consent change or asks for help.
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.
76
85
  #
77
86
  # These events state the signal outright, so you do not have to recognise keywords
78
87
  # in the text of a message.received event. They also cover cases that produce no
79
88
  # inbound message at all, such as a network handling an opt-out on your behalf.
80
89
  #
81
- # Fields are ordered identity → resulting state → provenance → join key. Nothing
82
- # here restates the envelope: which of the three signals occurred is the
83
- # envelope's event, and when it was emitted is its timestamp. Retries carry the
84
- # same X-Webhook-Event-ID header, which is what to deduplicate on.
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.
85
101
  payload: nil,
86
102
  # The event-specific body.
87
103
  request_id: nil,