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
@@ -24,6 +24,23 @@ module Sentdm
24
24
  sig { params(account_id: String).void }
25
25
  attr_writer :account_id
26
26
 
27
+ # Which consent keyword this template answers, when it is one of Sent's
28
+ # auto-replies: OPT_IN, OPT_OUT, HELP, or OTHER for a customer-defined keyword.
29
+ #
30
+ # Omitted for an ordinary template, so its presence is the answer to "is this an
31
+ # auto-reply". Sent creates the three compliance auto-replies at signup and they
32
+ # go through review like any other template, so their events arrive mixed in with
33
+ # the customer's own with nothing else to tell them apart.
34
+ #
35
+ # Named for the reader rather than after Template.OptAction, which it is mapped
36
+ # from. The MCP tool result deliberately keeps OptAction, OptKeywords and IsOpt:
37
+ # it mirrors the internal shape on purpose and publishes the keywords too, so
38
+ # renaming one of the three there would leave a surface half in each vocabulary.
39
+ # Two names for one concept, each consistent within its own surface, chosen over a
40
+ # rename that breaks MCP clients silently.
41
+ sig { returns(T.nilable(String)) }
42
+ attr_accessor :auto_reply_action
43
+
27
44
  # The template's category, for example UTILITY, MARKETING, or AUTHENTICATION.
28
45
  sig { returns(T.nilable(String)) }
29
46
  attr_reader :category
@@ -31,12 +48,15 @@ module Sentdm
31
48
  sig { params(category: String).void }
32
49
  attr_writer :category
33
50
 
34
- # The channel the template applies to.
51
+ # The channel leg this decision is about, for example whatsapp, sms, or rcs. A
52
+ # template is reviewed per channel and the legs come back independently, so each
53
+ # one reports separately.
54
+ #
55
+ # Omitted when the decision applies to the template as a whole rather than to one
56
+ # leg. That event is the broader news: a template-wide rejection blocks every
57
+ # channel, whatever the individual legs say.
35
58
  sig { returns(T.nilable(String)) }
36
- attr_reader :channel
37
-
38
- sig { params(channel: String).void }
39
- attr_writer :channel
59
+ attr_accessor :channel
40
60
 
41
61
  # The template's language code, for example en_US.
42
62
  sig { returns(T.nilable(String)) }
@@ -71,8 +91,9 @@ module Sentdm
71
91
  status: String,
72
92
  whatsapp_template_id: String,
73
93
  account_id: String,
94
+ auto_reply_action: T.nilable(String),
74
95
  category: String,
75
- channel: String,
96
+ channel: T.nilable(String),
76
97
  language: String,
77
98
  reason: T.nilable(String),
78
99
  template_id: String,
@@ -87,9 +108,30 @@ module Sentdm
87
108
  whatsapp_template_id:,
88
109
  # The account the template belongs to.
89
110
  account_id: nil,
111
+ # Which consent keyword this template answers, when it is one of Sent's
112
+ # auto-replies: OPT_IN, OPT_OUT, HELP, or OTHER for a customer-defined keyword.
113
+ #
114
+ # Omitted for an ordinary template, so its presence is the answer to "is this an
115
+ # auto-reply". Sent creates the three compliance auto-replies at signup and they
116
+ # go through review like any other template, so their events arrive mixed in with
117
+ # the customer's own with nothing else to tell them apart.
118
+ #
119
+ # Named for the reader rather than after Template.OptAction, which it is mapped
120
+ # from. The MCP tool result deliberately keeps OptAction, OptKeywords and IsOpt:
121
+ # it mirrors the internal shape on purpose and publishes the keywords too, so
122
+ # renaming one of the three there would leave a surface half in each vocabulary.
123
+ # Two names for one concept, each consistent within its own surface, chosen over a
124
+ # rename that breaks MCP clients silently.
125
+ auto_reply_action: nil,
90
126
  # The template's category, for example UTILITY, MARKETING, or AUTHENTICATION.
91
127
  category: nil,
92
- # The channel the template applies to.
128
+ # The channel leg this decision is about, for example whatsapp, sms, or rcs. A
129
+ # template is reviewed per channel and the legs come back independently, so each
130
+ # one reports separately.
131
+ #
132
+ # Omitted when the decision applies to the template as a whole rather than to one
133
+ # leg. That event is the broader news: a template-wide rejection blocks every
134
+ # channel, whatever the individual legs say.
93
135
  channel: nil,
94
136
  # The template's language code, for example en_US.
95
137
  language: nil,
@@ -109,8 +151,9 @@ module Sentdm
109
151
  status: String,
110
152
  whatsapp_template_id: String,
111
153
  account_id: String,
154
+ auto_reply_action: T.nilable(String),
112
155
  category: String,
113
- channel: String,
156
+ channel: T.nilable(String),
114
157
  language: String,
115
158
  reason: T.nilable(String),
116
159
  template_id: String,
@@ -13,7 +13,59 @@ module Sentdm
13
13
  sig { returns(String) }
14
14
  attr_accessor :template
15
15
 
16
- # The type of header (e.g., "text", "image", "video", "document")
16
+ # Request-only. The s.dm URL of the asset Meta's reviewers see —
17
+ # https://s.dm/s/{ID}, eight uppercase characters, uploaded to s.dm out of band.
18
+ # NormalizeRichHeader folds it into the synthesized media variable's Props.Sample
19
+ # and clears it, so it never persists and a stored definition is indistinguishable
20
+ # from an imported one.
21
+ #
22
+ # Stricter than the send path on purpose:
23
+ # TemplateUtils.ValidateMediaVariableValues accepts any absolute https URL for the
24
+ # per-send asset, because that one is the customer's and may live behind a signed
25
+ # CDN link. This one is the review sample, has to outlive every resubmission, and
26
+ # so must be ours. Do not "fix" one to match the other.
27
+ sig { returns(T.nilable(String)) }
28
+ attr_accessor :example_url
29
+
30
+ # The map pin a location header drops. Meta wants none of this at creation — the
31
+ # component is just {"type":"header","format":"location"} — so these values exist
32
+ # for Sent: a preview, and the default a StaticResource header falls back to at
33
+ # send.
34
+ sig { returns(T.nilable(Sentdm::TemplateHeader::Location)) }
35
+ attr_reader :location
36
+
37
+ sig do
38
+ params(
39
+ location: T.nilable(Sentdm::TemplateHeader::Location::OrHash)
40
+ ).void
41
+ end
42
+ attr_writer :location
43
+
44
+ # Whether the asset registered at creation is reused when a caller omits the
45
+ # header's variable at send time. Default false — the caller must supply it per
46
+ # message, which is the behaviour every existing template has. Written only when
47
+ # true, so a default-valued header serializes byte-identically to one imported
48
+ # from Meta.
49
+ #
50
+ # Stored and validated but not yet honoured at send: that lands with the Resumable
51
+ # Upload work, alongside the code that lets such a template be approved in the
52
+ # first place.
53
+ sig { returns(T.nilable(T::Boolean)) }
54
+ attr_reader :static_resource
55
+
56
+ sig { params(static_resource: T::Boolean).void }
57
+ attr_writer :static_resource
58
+
59
+ # The kind of header. One of:
60
+ #
61
+ # text — up to 60 characters, at most one variable. image — png, jpg or jpeg.
62
+ # Needs ExampleUrl. video — mp4. Needs ExampleUrl. gif — mp4, max 3.5MB. WhatsApp
63
+ # renders larger files as an ordinary video. Needs ExampleUrl. document — pdf or
64
+ # docx; only the first page is shown as a thumbnail, so pdf is the practical
65
+ # choice. Needs ExampleUrl. location — a map pin, supplied through Location.
66
+ #
67
+ # Kept lowercase because MetaToTemplateConverter writes Meta's format through
68
+ # ToLowerInvariant() into this field on import, and the two are compared directly.
17
69
  sig { returns(T.nilable(String)) }
18
70
  attr_accessor :type
19
71
 
@@ -25,6 +77,9 @@ module Sentdm
25
77
  sig do
26
78
  params(
27
79
  template: String,
80
+ example_url: T.nilable(String),
81
+ location: T.nilable(Sentdm::TemplateHeader::Location::OrHash),
82
+ static_resource: T::Boolean,
28
83
  type: T.nilable(String),
29
84
  variables: T.nilable(T::Array[Sentdm::TemplateVariable::OrHash])
30
85
  ).returns(T.attached_class)
@@ -33,7 +88,43 @@ module Sentdm
33
88
  # The header template text with optional variable placeholders (e.g., "Welcome to
34
89
  # {{0:variable}}")
35
90
  template:,
36
- # The type of header (e.g., "text", "image", "video", "document")
91
+ # Request-only. The s.dm URL of the asset Meta's reviewers see —
92
+ # https://s.dm/s/{ID}, eight uppercase characters, uploaded to s.dm out of band.
93
+ # NormalizeRichHeader folds it into the synthesized media variable's Props.Sample
94
+ # and clears it, so it never persists and a stored definition is indistinguishable
95
+ # from an imported one.
96
+ #
97
+ # Stricter than the send path on purpose:
98
+ # TemplateUtils.ValidateMediaVariableValues accepts any absolute https URL for the
99
+ # per-send asset, because that one is the customer's and may live behind a signed
100
+ # CDN link. This one is the review sample, has to outlive every resubmission, and
101
+ # so must be ours. Do not "fix" one to match the other.
102
+ example_url: nil,
103
+ # The map pin a location header drops. Meta wants none of this at creation — the
104
+ # component is just {"type":"header","format":"location"} — so these values exist
105
+ # for Sent: a preview, and the default a StaticResource header falls back to at
106
+ # send.
107
+ location: nil,
108
+ # Whether the asset registered at creation is reused when a caller omits the
109
+ # header's variable at send time. Default false — the caller must supply it per
110
+ # message, which is the behaviour every existing template has. Written only when
111
+ # true, so a default-valued header serializes byte-identically to one imported
112
+ # from Meta.
113
+ #
114
+ # Stored and validated but not yet honoured at send: that lands with the Resumable
115
+ # Upload work, alongside the code that lets such a template be approved in the
116
+ # first place.
117
+ static_resource: nil,
118
+ # The kind of header. One of:
119
+ #
120
+ # text — up to 60 characters, at most one variable. image — png, jpg or jpeg.
121
+ # Needs ExampleUrl. video — mp4. Needs ExampleUrl. gif — mp4, max 3.5MB. WhatsApp
122
+ # renders larger files as an ordinary video. Needs ExampleUrl. document — pdf or
123
+ # docx; only the first page is shown as a thumbnail, so pdf is the practical
124
+ # choice. Needs ExampleUrl. location — a map pin, supplied through Location.
125
+ #
126
+ # Kept lowercase because MetaToTemplateConverter writes Meta's format through
127
+ # ToLowerInvariant() into this field on import, and the two are compared directly.
37
128
  type: nil,
38
129
  # List of variables used in the header template
39
130
  variables: nil
@@ -44,6 +135,9 @@ module Sentdm
44
135
  override.returns(
45
136
  {
46
137
  template: String,
138
+ example_url: T.nilable(String),
139
+ location: T.nilable(Sentdm::TemplateHeader::Location),
140
+ static_resource: T::Boolean,
47
141
  type: T.nilable(String),
48
142
  variables: T.nilable(T::Array[Sentdm::TemplateVariable])
49
143
  }
@@ -51,6 +145,53 @@ module Sentdm
51
145
  end
52
146
  def to_hash
53
147
  end
148
+
149
+ class Location < Sentdm::Internal::Type::BaseModel
150
+ OrHash =
151
+ T.type_alias do
152
+ T.any(Sentdm::TemplateHeader::Location, Sentdm::Internal::AnyHash)
153
+ end
154
+
155
+ sig { returns(String) }
156
+ attr_accessor :address
157
+
158
+ sig { returns(String) }
159
+ attr_accessor :latitude
160
+
161
+ sig { returns(String) }
162
+ attr_accessor :longitude
163
+
164
+ sig { returns(String) }
165
+ attr_accessor :name
166
+
167
+ # The map pin a location header drops. Meta wants none of this at creation — the
168
+ # component is just {"type":"header","format":"location"} — so these values exist
169
+ # for Sent: a preview, and the default a StaticResource header falls back to at
170
+ # send.
171
+ sig do
172
+ params(
173
+ address: String,
174
+ latitude: String,
175
+ longitude: String,
176
+ name: String
177
+ ).returns(T.attached_class)
178
+ end
179
+ def self.new(address:, latitude:, longitude:, name:)
180
+ end
181
+
182
+ sig do
183
+ override.returns(
184
+ {
185
+ address: String,
186
+ latitude: String,
187
+ longitude: String,
188
+ name: String
189
+ }
190
+ )
191
+ end
192
+ def to_hash
193
+ end
194
+ end
54
195
  end
55
196
  end
56
197
  end
@@ -8,6 +8,9 @@ module Sentdm
8
8
  T.any(Sentdm::TemplateVariable, Sentdm::Internal::AnyHash)
9
9
  end
10
10
 
11
+ # The variable's name, and the key callers use for it in a send request's
12
+ # parameters object. Must start with a letter and hold only letters, digits and
13
+ # underscores.
11
14
  sig { returns(String) }
12
15
  attr_accessor :name
13
16
 
@@ -17,9 +20,17 @@ module Sentdm
17
20
  sig { params(props: Sentdm::TemplateVariable::Props::OrHash).void }
18
21
  attr_writer :props
19
22
 
23
+ # One of variable, link or media. Decides which Props fields are required.
20
24
  sig { returns(String) }
21
25
  attr_accessor :type
22
26
 
27
+ # The variable's index, and the number its {{index:variable}} placeholder refers
28
+ # to.
29
+ #
30
+ # Omitting it is only safe for a section holding a single variable. The field is a
31
+ # non-nullable int, so every variable that leaves it out defaults to 0, and a
32
+ # section with two such variables is refused by the unique-id rule ("variables
33
+ # must have unique IDs"). Number them from 0 in the order they appear.
23
34
  sig { returns(T.nilable(Integer)) }
24
35
  attr_reader :id
25
36
 
@@ -34,7 +45,23 @@ module Sentdm
34
45
  id: Integer
35
46
  ).returns(T.attached_class)
36
47
  end
37
- def self.new(name:, props:, type:, id: nil)
48
+ def self.new(
49
+ # The variable's name, and the key callers use for it in a send request's
50
+ # parameters object. Must start with a letter and hold only letters, digits and
51
+ # underscores.
52
+ name:,
53
+ props:,
54
+ # One of variable, link or media. Decides which Props fields are required.
55
+ type:,
56
+ # The variable's index, and the number its {{index:variable}} placeholder refers
57
+ # to.
58
+ #
59
+ # Omitting it is only safe for a section holding a single variable. The field is a
60
+ # non-nullable int, so every variable that leaves it out defaults to 0, and a
61
+ # section with two such variables is refused by the unique-id rule ("variables
62
+ # must have unique IDs"). Number them from 0 in the order they appear.
63
+ id: nil
64
+ )
38
65
  end
39
66
 
40
67
  sig do
@@ -59,6 +86,11 @@ module Sentdm
59
86
  sig { returns(String) }
60
87
  attr_accessor :media_type
61
88
 
89
+ # Example value substituted into the template when previewing it and when
90
+ # submitting it to Meta for review. Free text by nature, so the converter accepts
91
+ # a JSON number or boolean here and normalizes it — see
92
+ # JsonScalarToStringConverter for why — and guarantees it is always serialized
93
+ # back out as a JSON string.
62
94
  sig { returns(String) }
63
95
  attr_accessor :sample
64
96
 
@@ -90,6 +122,11 @@ module Sentdm
90
122
  end
91
123
  def self.new(
92
124
  media_type:,
125
+ # Example value substituted into the template when previewing it and when
126
+ # submitting it to Meta for review. Free text by nature, so the converter accepts
127
+ # a JSON number or boolean here and normalizes it — see
128
+ # JsonScalarToStringConverter for why — and guarantees it is always serialized
129
+ # back out as a JSON string.
93
130
  sample:,
94
131
  url:,
95
132
  variable_type:,