sentdm 0.31.0 → 0.32.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +1 -1
  4. data/lib/sentdm/models/channel_event.rb +78 -0
  5. data/lib/sentdm/models/channel_event_payload.rb +128 -0
  6. data/lib/sentdm/models/contact_event.rb +69 -0
  7. data/lib/sentdm/models/contact_event_payload.rb +111 -0
  8. data/lib/sentdm/models/inbound_message_event.rb +16 -8
  9. data/lib/sentdm/models/message_event.rb +16 -8
  10. data/lib/sentdm/models/message_event_payload.rb +11 -1
  11. data/lib/sentdm/models/template.rb +36 -5
  12. data/lib/sentdm/models/template_body.rb +20 -11
  13. data/lib/sentdm/models/template_body_content.rb +35 -3
  14. data/lib/sentdm/models/template_button.rb +8 -2
  15. data/lib/sentdm/models/template_button_props.rb +12 -1
  16. data/lib/sentdm/models/template_definition.rb +14 -2
  17. data/lib/sentdm/models/template_event.rb +16 -8
  18. data/lib/sentdm/models/template_event_payload.rb +31 -4
  19. data/lib/sentdm/models/template_variable.rb +20 -3
  20. data/lib/sentdm/models/webhook_list_events_response.rb +17 -9
  21. data/lib/sentdm/models.rb +8 -0
  22. data/lib/sentdm/resources/templates.rb +28 -2
  23. data/lib/sentdm/version.rb +1 -1
  24. data/lib/sentdm.rb +4 -0
  25. data/rbi/sentdm/models/channel_event.rbi +128 -0
  26. data/rbi/sentdm/models/channel_event_payload.rbi +189 -0
  27. data/rbi/sentdm/models/contact_event.rbi +110 -0
  28. data/rbi/sentdm/models/contact_event_payload.rbi +164 -0
  29. data/rbi/sentdm/models/inbound_message_event.rbi +18 -10
  30. data/rbi/sentdm/models/message_event.rbi +18 -10
  31. data/rbi/sentdm/models/message_event_payload.rbi +12 -0
  32. data/rbi/sentdm/models/template.rbi +58 -4
  33. data/rbi/sentdm/models/template_body.rbi +22 -13
  34. data/rbi/sentdm/models/template_body_content.rbi +59 -1
  35. data/rbi/sentdm/models/template_button.rbi +14 -2
  36. data/rbi/sentdm/models/template_button_props.rbi +22 -0
  37. data/rbi/sentdm/models/template_definition.rbi +20 -2
  38. data/rbi/sentdm/models/template_event.rbi +18 -10
  39. data/rbi/sentdm/models/template_event_payload.rbi +51 -8
  40. data/rbi/sentdm/models/template_variable.rbi +28 -1
  41. data/rbi/sentdm/models/webhook_list_events_response.rbi +18 -12
  42. data/rbi/sentdm/models.rbi +8 -0
  43. data/rbi/sentdm/resources/templates.rbi +28 -2
  44. data/sig/sentdm/models/channel_event.rbs +44 -0
  45. data/sig/sentdm/models/channel_event_payload.rbs +63 -0
  46. data/sig/sentdm/models/contact_event.rbs +44 -0
  47. data/sig/sentdm/models/contact_event_payload.rbs +63 -0
  48. data/sig/sentdm/models/inbound_message_event.rbs +5 -0
  49. data/sig/sentdm/models/message_event.rbs +5 -0
  50. data/sig/sentdm/models/message_event_payload.rbs +5 -0
  51. data/sig/sentdm/models/template.rbs +5 -0
  52. data/sig/sentdm/models/template_event.rbs +5 -0
  53. data/sig/sentdm/models/template_event_payload.rbs +9 -6
  54. data/sig/sentdm/models/webhook_list_events_response.rbs +2 -0
  55. data/sig/sentdm/models.rbs +8 -0
  56. metadata +14 -2
@@ -18,6 +18,21 @@ module Sentdm
18
18
  sig { params(id: String).void }
19
19
  attr_writer :id
20
20
 
21
+ # Which consent keyword this template answers, when it is one of Sent's
22
+ # auto-replies: OPT_IN, OPT_OUT, HELP, or OTHER for a customer-defined keyword.
23
+ # Null for an ordinary template, and omitted from the response, so its presence is
24
+ # the answer to "is this an auto-reply".
25
+ #
26
+ # Deliberately not required, unlike CustomerId, even though the same "no single
27
+ # mapper" argument applies: NJsonSchema publishes a C# required member in the
28
+ # schema's required array, so the contract would have advertised a field this
29
+ # response omits for every ordinary template, and a generated client could refuse
30
+ # the common case. A compile-time guard is not worth a wrong published contract.
31
+ # Every mapping site sets it explicitly, and TemplateResponseSchemaTests pins the
32
+ # field as optional so it cannot be reintroduced.
33
+ sig { returns(T.nilable(String)) }
34
+ attr_accessor :auto_reply_action
35
+
21
36
  # Template category: MARKETING, UTILITY, AUTHENTICATION
22
37
  sig { returns(T.nilable(String)) }
23
38
  attr_reader :category
@@ -25,7 +40,18 @@ module Sentdm
25
40
  sig { params(category: String).void }
26
41
  attr_writer :category
27
42
 
28
- # Supported channels: sms, whatsapp
43
+ # The channels this template's definition can render on, in canonical order: sms,
44
+ # whatsapp, rcs.
45
+ #
46
+ # Derived from the definition's body, mirroring each channel's send-time fallback
47
+ # chain, so a channel is listed only when a real body would be produced for it:
48
+ # SMS reads sms ?? multiChannel, WhatsApp reads whatsapp ?? multiChannel, and RCS
49
+ # reads rcs ?? multiChannel ?? sms. A multiChannel body therefore reports all
50
+ # three, and the extra SMS fallback on RCS is why an sms/whatsapp pair reports RCS
51
+ # too.
52
+ #
53
+ # This says what the content can render on, not what may be sent: sending also
54
+ # needs the template approved for that channel.
29
55
  sig { returns(T.nilable(T::Array[String])) }
30
56
  attr_accessor :channels
31
57
 
@@ -57,7 +83,8 @@ module Sentdm
57
83
  sig { params(name: String).void }
58
84
  attr_writer :name
59
85
 
60
- # Template status: APPROVED, PENDING, REJECTED
86
+ # Template status: DRAFT, PENDING, APPROVED, REJECTED. A template created with
87
+ # submit_for_review: false starts as DRAFT and stays there until it is submitted.
61
88
  sig { returns(T.nilable(String)) }
62
89
  attr_reader :status
63
90
 
@@ -77,6 +104,7 @@ module Sentdm
77
104
  params(
78
105
  customer_id: String,
79
106
  id: String,
107
+ auto_reply_action: T.nilable(String),
80
108
  category: String,
81
109
  channels: T.nilable(T::Array[String]),
82
110
  created_at: Time,
@@ -94,9 +122,33 @@ module Sentdm
94
122
  customer_id:,
95
123
  # Unique template identifier
96
124
  id: nil,
125
+ # Which consent keyword this template answers, when it is one of Sent's
126
+ # auto-replies: OPT_IN, OPT_OUT, HELP, or OTHER for a customer-defined keyword.
127
+ # Null for an ordinary template, and omitted from the response, so its presence is
128
+ # the answer to "is this an auto-reply".
129
+ #
130
+ # Deliberately not required, unlike CustomerId, even though the same "no single
131
+ # mapper" argument applies: NJsonSchema publishes a C# required member in the
132
+ # schema's required array, so the contract would have advertised a field this
133
+ # response omits for every ordinary template, and a generated client could refuse
134
+ # the common case. A compile-time guard is not worth a wrong published contract.
135
+ # Every mapping site sets it explicitly, and TemplateResponseSchemaTests pins the
136
+ # field as optional so it cannot be reintroduced.
137
+ auto_reply_action: nil,
97
138
  # Template category: MARKETING, UTILITY, AUTHENTICATION
98
139
  category: nil,
99
- # Supported channels: sms, whatsapp
140
+ # The channels this template's definition can render on, in canonical order: sms,
141
+ # whatsapp, rcs.
142
+ #
143
+ # Derived from the definition's body, mirroring each channel's send-time fallback
144
+ # chain, so a channel is listed only when a real body would be produced for it:
145
+ # SMS reads sms ?? multiChannel, WhatsApp reads whatsapp ?? multiChannel, and RCS
146
+ # reads rcs ?? multiChannel ?? sms. A multiChannel body therefore reports all
147
+ # three, and the extra SMS fallback on RCS is why an sms/whatsapp pair reports RCS
148
+ # too.
149
+ #
150
+ # This says what the content can render on, not what may be sent: sending also
151
+ # needs the template approved for that channel.
100
152
  channels: nil,
101
153
  # When the template was created
102
154
  created_at: nil,
@@ -106,7 +158,8 @@ module Sentdm
106
158
  language: nil,
107
159
  # Template display name
108
160
  name: nil,
109
- # Template status: APPROVED, PENDING, REJECTED
161
+ # Template status: DRAFT, PENDING, APPROVED, REJECTED. A template created with
162
+ # submit_for_review: false starts as DRAFT and stays there until it is submitted.
110
163
  status: nil,
111
164
  # When the template was last updated
112
165
  updated_at: nil,
@@ -120,6 +173,7 @@ module Sentdm
120
173
  {
121
174
  customer_id: String,
122
175
  id: String,
176
+ auto_reply_action: T.nilable(String),
123
177
  category: String,
124
178
  channels: T.nilable(T::Array[String]),
125
179
  created_at: Time,
@@ -6,8 +6,7 @@ module Sentdm
6
6
  OrHash =
7
7
  T.type_alias { T.any(Sentdm::TemplateBody, Sentdm::Internal::AnyHash) }
8
8
 
9
- # Content that will be used for all channels (SMS and WhatsApp) unless
10
- # channel-specific content is provided
9
+ # The shared body, used for every channel. One half of the choice described above.
11
10
  sig { returns(T.nilable(Sentdm::TemplateBodyContent)) }
12
11
  attr_reader :multi_channel
13
12
 
@@ -18,22 +17,23 @@ module Sentdm
18
17
  end
19
18
  attr_writer :multi_channel
20
19
 
21
- # RCS-specific content that overrides multi-channel content for RCS messages
20
+ # RCS-specific copy that overrides the chosen strategy for RCS only. The one true
21
+ # override: optional on top of either strategy, but it cannot be the only body
22
+ # present. Its length cap is the higher one described on Template.
22
23
  sig { returns(T.nilable(Sentdm::TemplateBodyContent)) }
23
24
  attr_reader :rcs
24
25
 
25
26
  sig { params(rcs: T.nilable(Sentdm::TemplateBodyContent::OrHash)).void }
26
27
  attr_writer :rcs
27
28
 
28
- # SMS-specific content that overrides multi-channel content for SMS messages
29
+ # The SMS body. It does not override multiChannel, it replaces it.
29
30
  sig { returns(T.nilable(Sentdm::TemplateBodyContent)) }
30
31
  attr_reader :sms
31
32
 
32
33
  sig { params(sms: T.nilable(Sentdm::TemplateBodyContent::OrHash)).void }
33
34
  attr_writer :sms
34
35
 
35
- # WhatsApp-specific content that overrides multi-channel content for WhatsApp
36
- # messages
36
+ # The WhatsApp body. It does not override multiChannel, it replaces it.
37
37
  sig { returns(T.nilable(Sentdm::TemplateBodyContent)) }
38
38
  attr_reader :whatsapp
39
39
 
@@ -42,7 +42,16 @@ module Sentdm
42
42
  end
43
43
  attr_writer :whatsapp
44
44
 
45
- # Body section of a message template with channel-specific content
45
+ # Body section of a message template.
46
+ #
47
+ # A body picks one of two authoring strategies, and mixing them is refused
48
+ # (TemplateDefinitionValidator.HaveValidChannelConfiguration): a shared
49
+ # multiChannel body on its own, or an explicit sms + whatsapp pair, both present.
50
+ #
51
+ # multiChannel together with sms or whatsapp is rejected, and so is sms or
52
+ # whatsapp on its own — every template is expected to be deliverable on every
53
+ # channel. rcs is the one true override: it may accompany either strategy to vary
54
+ # the copy, but cannot stand alone.
46
55
  sig do
47
56
  params(
48
57
  multi_channel: T.nilable(Sentdm::TemplateBodyContent::OrHash),
@@ -52,15 +61,15 @@ module Sentdm
52
61
  ).returns(T.attached_class)
53
62
  end
54
63
  def self.new(
55
- # Content that will be used for all channels (SMS and WhatsApp) unless
56
- # channel-specific content is provided
64
+ # The shared body, used for every channel. One half of the choice described above.
57
65
  multi_channel: nil,
58
- # RCS-specific content that overrides multi-channel content for RCS messages
66
+ # RCS-specific copy that overrides the chosen strategy for RCS only. The one true
67
+ # override: optional on top of either strategy, but it cannot be the only body
68
+ # present. Its length cap is the higher one described on Template.
59
69
  rcs: nil,
60
- # SMS-specific content that overrides multi-channel content for SMS messages
70
+ # The SMS body. It does not override multiChannel, it replaces it.
61
71
  sms: nil,
62
- # WhatsApp-specific content that overrides multi-channel content for WhatsApp
63
- # messages
72
+ # The WhatsApp body. It does not override multiChannel, it replaces it.
64
73
  whatsapp: nil
65
74
  )
66
75
  end
@@ -8,12 +8,39 @@ module Sentdm
8
8
  T.any(Sentdm::TemplateBodyContent, Sentdm::Internal::AnyHash)
9
9
  end
10
10
 
11
+ # The body copy, with variables written as {{index:variable}}.
12
+ #
13
+ # Length cap depends on which channel this body belongs to:
14
+ # TemplateContentLimits.MaxBodyLength (1024) for multiChannel, sms and whatsapp —
15
+ # Meta's BODY limit, which a multiChannel body may be delivered under — and
16
+ # TemplateContentLimits.MaxRcsBodyLength (3072) for an rcs body, which never
17
+ # reaches Meta. The maxLength advertised on this schema is the 1024 one, because
18
+ # all four channel bodies share this single schema — an rcs body between the two
19
+ # is accepted.
20
+ #
21
+ # Meta requires every variable to carry surrounding context, so a body is refused
22
+ # unless it also satisfies all of the following (enforced by
23
+ # TemplateDefinitionValidator): At least one letter before the first variable and
24
+ # after the last — trailing punctuation such as "... {{1:variable}}." does not
25
+ # count. At least (2 × variable count) + 1 words once the placeholders are
26
+ # removed. No two variables adjacent with only whitespace between them. No leading
27
+ # or trailing newline, no more than two consecutive line breaks, and no more than
28
+ # four consecutive spaces.
29
+ #
30
+ # Example: "Hello {{0:variable}}! Welcome to {{1:variable}}. We are glad to have
31
+ # you on board." — two variables, so at least five words are required, and the
32
+ # copy after the final variable contains letters.
11
33
  sig { returns(String) }
12
34
  attr_accessor :template
13
35
 
36
+ # The type of body content — send "text". It is dropped from the stored definition
37
+ # when null, so a body posted without it is saved with no type key at all and the
38
+ # template editor has nothing to render the block from.
14
39
  sig { returns(T.nilable(String)) }
15
40
  attr_accessor :type
16
41
 
42
+ # The variables referenced by the body copy, one entry per {{index:variable}}
43
+ # placeholder.
17
44
  sig { returns(T.nilable(T::Array[Sentdm::TemplateVariable])) }
18
45
  attr_accessor :variables
19
46
 
@@ -24,7 +51,38 @@ module Sentdm
24
51
  variables: T.nilable(T::Array[Sentdm::TemplateVariable::OrHash])
25
52
  ).returns(T.attached_class)
26
53
  end
27
- def self.new(template:, type: nil, variables: nil)
54
+ def self.new(
55
+ # The body copy, with variables written as {{index:variable}}.
56
+ #
57
+ # Length cap depends on which channel this body belongs to:
58
+ # TemplateContentLimits.MaxBodyLength (1024) for multiChannel, sms and whatsapp —
59
+ # Meta's BODY limit, which a multiChannel body may be delivered under — and
60
+ # TemplateContentLimits.MaxRcsBodyLength (3072) for an rcs body, which never
61
+ # reaches Meta. The maxLength advertised on this schema is the 1024 one, because
62
+ # all four channel bodies share this single schema — an rcs body between the two
63
+ # is accepted.
64
+ #
65
+ # Meta requires every variable to carry surrounding context, so a body is refused
66
+ # unless it also satisfies all of the following (enforced by
67
+ # TemplateDefinitionValidator): At least one letter before the first variable and
68
+ # after the last — trailing punctuation such as "... {{1:variable}}." does not
69
+ # count. At least (2 × variable count) + 1 words once the placeholders are
70
+ # removed. No two variables adjacent with only whitespace between them. No leading
71
+ # or trailing newline, no more than two consecutive line breaks, and no more than
72
+ # four consecutive spaces.
73
+ #
74
+ # Example: "Hello {{0:variable}}! Welcome to {{1:variable}}. We are glad to have
75
+ # you on board." — two variables, so at least five words are required, and the
76
+ # copy after the final variable contains letters.
77
+ template:,
78
+ # The type of body content — send "text". It is dropped from the stored definition
79
+ # when null, so a body posted without it is saved with no type key at all and the
80
+ # template editor has nothing to render the block from.
81
+ type: nil,
82
+ # The variables referenced by the body copy, one entry per {{index:variable}}
83
+ # placeholder.
84
+ variables: nil
85
+ )
28
86
  end
29
87
 
30
88
  sig do
@@ -19,7 +19,13 @@ module Sentdm
19
19
  sig { returns(String) }
20
20
  attr_accessor :type
21
21
 
22
- # The unique identifier of the button (1-based index)
22
+ # The button's identifier (1-based index), unique within the template.
23
+ #
24
+ # Omitting it is only safe for a template holding a single button. The field is a
25
+ # non-nullable int, so every button that leaves it out defaults to 0, and two such
26
+ # buttons are refused by the unique-id rule ("Button IDs must be unique"). Number
27
+ # them from 1 in the order they should appear — order matters on RCS, where only
28
+ # the first four buttons render.
23
29
  sig { returns(T.nilable(Integer)) }
24
30
  attr_reader :id
25
31
 
@@ -39,7 +45,13 @@ module Sentdm
39
45
  props:,
40
46
  # The type of button (e.g., QUICK_REPLY, URL, PHONE_NUMBER, VOICE_CALL, COPY_CODE)
41
47
  type:,
42
- # The unique identifier of the button (1-based index)
48
+ # The button's identifier (1-based index), unique within the template.
49
+ #
50
+ # Omitting it is only safe for a template holding a single button. The field is a
51
+ # non-nullable int, so every button that leaves it out defaults to 0, and two such
52
+ # buttons are refused by the unique-id rule ("Button IDs must be unique"). Number
53
+ # them from 1 in the order they should appear — order matters on RCS, where only
54
+ # the first four buttons render.
43
55
  id: nil
44
56
  )
45
57
  end
@@ -23,6 +23,17 @@ module Sentdm
23
23
  sig { returns(String) }
24
24
  attr_accessor :quick_reply_type
25
25
 
26
+ # The button's label. Required for every button type, and capped at
27
+ # TemplateContentLimits.MaxButtonTextLength (25) characters.
28
+ #
29
+ # Meta accepts only static text here, so a label is refused when it contains a
30
+ # {{...}} variable placeholder, a newline, an emoji, or WhatsApp formatting markup
31
+ # (\*, \_, ~) — enforced by ApplyButtonLabelContentRules in
32
+ # TemplateButtonValidator. Meta reports all four as one error: "Buttons can't have
33
+ # any variables, newlines, emojis, or formatting characters."
34
+ #
35
+ # AUTHENTICATION OTP buttons are the exception: Meta auto-localizes their label
36
+ # from the template language, and the converter drops whatever text was sent.
26
37
  sig { returns(String) }
27
38
  attr_accessor :text
28
39
 
@@ -73,6 +84,17 @@ module Sentdm
73
84
  offer_code:,
74
85
  phone_number:,
75
86
  quick_reply_type:,
87
+ # The button's label. Required for every button type, and capped at
88
+ # TemplateContentLimits.MaxButtonTextLength (25) characters.
89
+ #
90
+ # Meta accepts only static text here, so a label is refused when it contains a
91
+ # {{...}} variable placeholder, a newline, an emoji, or WhatsApp formatting markup
92
+ # (\*, \_, ~) — enforced by ApplyButtonLabelContentRules in
93
+ # TemplateButtonValidator. Meta reports all four as one error: "Buttons can't have
94
+ # any variables, newlines, emojis, or formatting characters."
95
+ #
96
+ # AUTHENTICATION OTP buttons are the exception: Meta auto-localizes their label
97
+ # from the template language, and the converter drops whatever text was sent.
76
98
  text:,
77
99
  url:,
78
100
  url_type:,
@@ -8,7 +8,16 @@ module Sentdm
8
8
  T.any(Sentdm::TemplateDefinition, Sentdm::Internal::AnyHash)
9
9
  end
10
10
 
11
- # Body section of a message template with channel-specific content
11
+ # Body section of a message template.
12
+ #
13
+ # A body picks one of two authoring strategies, and mixing them is refused
14
+ # (TemplateDefinitionValidator.HaveValidChannelConfiguration): a shared
15
+ # multiChannel body on its own, or an explicit sms + whatsapp pair, both present.
16
+ #
17
+ # multiChannel together with sms or whatsapp is rejected, and so is sms or
18
+ # whatsapp on its own — every template is expected to be deliverable on every
19
+ # channel. rcs is the one true override: it may accompany either strategy to vary
20
+ # the copy, but cannot stand alone.
12
21
  sig { returns(Sentdm::TemplateBody) }
13
22
  attr_reader :body
14
23
 
@@ -62,7 +71,16 @@ module Sentdm
62
71
  ).returns(T.attached_class)
63
72
  end
64
73
  def self.new(
65
- # Body section of a message template with channel-specific content
74
+ # Body section of a message template.
75
+ #
76
+ # A body picks one of two authoring strategies, and mixing them is refused
77
+ # (TemplateDefinitionValidator.HaveValidChannelConfiguration): a shared
78
+ # multiChannel body on its own, or an explicit sms + whatsapp pair, both present.
79
+ #
80
+ # multiChannel together with sms or whatsapp is rejected, and so is sms or
81
+ # whatsapp on its own — every template is expected to be deliverable on every
82
+ # channel. rcs is the one true override: it may accompany either strategy to vary
83
+ # the copy, but cannot stand alone.
66
84
  body:,
67
85
  # Configuration for AUTHENTICATION category templates
68
86
  authentication_config: nil,
@@ -6,14 +6,14 @@ module Sentdm
6
6
  OrHash =
7
7
  T.type_alias { T.any(Sentdm::TemplateEvent, Sentdm::Internal::AnyHash) }
8
8
 
9
- # The specific event within the family, for example message.delivered or
10
- # message.received. Absent on events that have no subtype, so treat it as
11
- # optional.
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
12
  sig { returns(T.nilable(String)) }
13
13
  attr_accessor :event
14
14
 
15
- # The event family, for example message or templates. Route on this first, then on
16
- # event for the specific change.
15
+ # The event family, for example message, templates or contact. Route on this
16
+ # first, then on event for the specific change.
17
17
  sig { returns(T.nilable(String)) }
18
18
  attr_reader :field
19
19
 
@@ -30,6 +30,10 @@ module Sentdm
30
30
  end
31
31
  attr_writer :payload
32
32
 
33
+ # The event-specific body.
34
+ sig { returns(T.nilable(String)) }
35
+ attr_accessor :request_id
36
+
33
37
  # When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
34
38
  # time, not the time the underlying change happened. Use the timestamp inside the
35
39
  # payload for the latter.
@@ -46,20 +50,23 @@ module Sentdm
46
50
  event: T.nilable(String),
47
51
  field: String,
48
52
  payload: T.nilable(Sentdm::TemplateEventPayload::OrHash),
53
+ request_id: T.nilable(String),
49
54
  timestamp: String
50
55
  ).returns(T.attached_class)
51
56
  end
52
57
  def self.new(
53
- # The specific event within the family, for example message.delivered or
54
- # message.received. Absent on events that have no subtype, so treat it as
55
- # optional.
58
+ # The specific event within the family, for example message.delivered,
59
+ # message.received or contact.opt_out. Absent on events that have no subtype, so
60
+ # treat it as optional.
56
61
  event: nil,
57
- # The event family, for example message or templates. Route on this first, then on
58
- # event for the specific change.
62
+ # The event family, for example message, templates or contact. Route on this
63
+ # first, then on event for the specific change.
59
64
  field: nil,
60
65
  # Body of a template status event. Delivered when a template's review outcome
61
66
  # changes, so you can react without polling.
62
67
  payload: nil,
68
+ # The event-specific body.
69
+ request_id: nil,
63
70
  # When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
64
71
  # time, not the time the underlying change happened. Use the timestamp inside the
65
72
  # payload for the latter.
@@ -73,6 +80,7 @@ module Sentdm
73
80
  event: T.nilable(String),
74
81
  field: String,
75
82
  payload: T.nilable(Sentdm::TemplateEventPayload),
83
+ request_id: T.nilable(String),
76
84
  timestamp: String
77
85
  }
78
86
  )
@@ -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,
@@ -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
@@ -39,9 +39,9 @@ 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 four webhook envelopes: a message status change, an inbound message, a
43
+ # template status change, or a contact consent signal. Read field and event to
44
+ # tell which, the same way your endpoint does.
45
45
  sig do
46
46
  returns(
47
47
  T.nilable(
@@ -57,7 +57,9 @@ module Sentdm
57
57
  T.any(
58
58
  Sentdm::MessageEvent::OrHash,
59
59
  Sentdm::InboundMessageEvent::OrHash,
60
- Sentdm::TemplateEvent::OrHash
60
+ Sentdm::TemplateEvent::OrHash,
61
+ Sentdm::ChannelEvent::OrHash,
62
+ Sentdm::ContactEvent::OrHash
61
63
  )
62
64
  ).void
63
65
  end
@@ -92,7 +94,9 @@ module Sentdm
92
94
  T.any(
93
95
  Sentdm::MessageEvent::OrHash,
94
96
  Sentdm::InboundMessageEvent::OrHash,
95
- Sentdm::TemplateEvent::OrHash
97
+ Sentdm::TemplateEvent::OrHash,
98
+ Sentdm::ChannelEvent::OrHash,
99
+ Sentdm::ContactEvent::OrHash
96
100
  ),
97
101
  event_type: String,
98
102
  http_status_code: T.nilable(Integer),
@@ -108,9 +112,9 @@ module Sentdm
108
112
  delivery_status: nil,
109
113
  error_message: nil,
110
114
  # 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.
115
+ # the four webhook envelopes: a message status change, an inbound message, a
116
+ # template status change, or a contact consent signal. Read field and event to
117
+ # tell which, the same way your endpoint does.
114
118
  event_data: nil,
115
119
  event_type: nil,
116
120
  http_status_code: nil,
@@ -142,9 +146,9 @@ module Sentdm
142
146
  end
143
147
 
144
148
  # 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.
149
+ # the four webhook envelopes: a message status change, an inbound message, a
150
+ # template status change, or a contact consent signal. Read field and event to
151
+ # tell which, the same way your endpoint does.
148
152
  module EventData
149
153
  extend Sentdm::Internal::Type::Union
150
154
 
@@ -153,7 +157,9 @@ module Sentdm
153
157
  T.any(
154
158
  Sentdm::MessageEvent,
155
159
  Sentdm::InboundMessageEvent,
156
- Sentdm::TemplateEvent
160
+ Sentdm::TemplateEvent,
161
+ Sentdm::ChannelEvent,
162
+ Sentdm::ContactEvent
157
163
  )
158
164
  end
159
165