late-sdk 0.0.916 → 0.0.918

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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/docs/AdCampaignsApi.md +1 -1
  4. data/docs/CreateInboxConversationRequest.md +2 -0
  5. data/docs/CreateInboxConversationRequestTemplateCardsInner.md +24 -0
  6. data/docs/CreateInboxConversationRequestTemplateCardsInnerButtonsInner.md +22 -0
  7. data/docs/CreateInboxConversationRequestTemplateCardsInnerHeaderMedia.md +22 -0
  8. data/docs/MessagesApi.md +1 -1
  9. data/lib/zernio-sdk/api/ad_campaigns_api.rb +2 -2
  10. data/lib/zernio-sdk/api/messages_api.rb +2 -2
  11. data/lib/zernio-sdk/models/create_inbox_conversation400_response.rb +2 -2
  12. data/lib/zernio-sdk/models/create_inbox_conversation_request.rb +32 -1
  13. data/lib/zernio-sdk/models/create_inbox_conversation_request_template_cards_inner.rb +235 -0
  14. data/lib/zernio-sdk/models/create_inbox_conversation_request_template_cards_inner_buttons_inner.rb +270 -0
  15. data/lib/zernio-sdk/models/create_inbox_conversation_request_template_cards_inner_header_media.rb +210 -0
  16. data/lib/zernio-sdk/version.rb +1 -1
  17. data/lib/zernio-sdk.rb +3 -0
  18. data/openapi.yaml +63 -2
  19. data/spec/api/ad_campaigns_api_spec.rb +1 -1
  20. data/spec/api/messages_api_spec.rb +1 -1
  21. data/spec/models/create_inbox_conversation400_response_spec.rb +1 -1
  22. data/spec/models/create_inbox_conversation_request_spec.rb +6 -0
  23. data/spec/models/create_inbox_conversation_request_template_cards_inner_buttons_inner_spec.rb +52 -0
  24. data/spec/models/create_inbox_conversation_request_template_cards_inner_header_media_spec.rb +52 -0
  25. data/spec/models/create_inbox_conversation_request_template_cards_inner_spec.rb +54 -0
  26. data/zernio-sdk-0.0.918.gem +0 -0
  27. metadata +14 -2
  28. data/zernio-sdk-0.0.916.gem +0 -0
@@ -0,0 +1,270 @@
1
+ =begin
2
+ #Zernio API
3
+
4
+ #API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api Versioning and deprecation: all endpoints are versioned in the URL path (current version: /v1). Breaking changes only ship in a new path version; existing versions keep working. Deprecated operations are marked 'deprecated: true' in this spec and announced in the changelog (https://zernio.com/changelog) before removal. Errors: every 4xx/5xx response is application/json with a machine-readable 'code' and a human-readable 'error' message (see the ErrorResponse schema).
5
+
6
+ The version of the OpenAPI document: 1.0.4
7
+ Contact: support@zernio.com
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.19.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Zernio
17
+ class CreateInboxConversationRequestTemplateCardsInnerButtonsInner < ApiModelBase
18
+ # Zero-based position of the button within the card's buttons.
19
+ attr_accessor :index
20
+
21
+ # The button kind, which decides how the value is sent.
22
+ attr_accessor :sub_type
23
+
24
+ # The value to send (quick_reply payload, or the URL dynamic suffix).
25
+ attr_accessor :value
26
+
27
+ class EnumAttributeValidator
28
+ attr_reader :datatype
29
+ attr_reader :allowable_values
30
+
31
+ def initialize(datatype, allowable_values)
32
+ @allowable_values = allowable_values.map do |value|
33
+ case datatype.to_s
34
+ when /Integer/i
35
+ value.to_i
36
+ when /Float/i
37
+ value.to_f
38
+ else
39
+ value
40
+ end
41
+ end
42
+ end
43
+
44
+ def valid?(value)
45
+ !value || allowable_values.include?(value)
46
+ end
47
+ end
48
+
49
+ # Attribute mapping from ruby-style variable name to JSON key.
50
+ def self.attribute_map
51
+ {
52
+ :'index' => :'index',
53
+ :'sub_type' => :'subType',
54
+ :'value' => :'value'
55
+ }
56
+ end
57
+
58
+ # Returns attribute mapping this model knows about
59
+ def self.acceptable_attribute_map
60
+ attribute_map
61
+ end
62
+
63
+ # Returns all the JSON keys this model knows about
64
+ def self.acceptable_attributes
65
+ acceptable_attribute_map.values
66
+ end
67
+
68
+ # Attribute type mapping.
69
+ def self.openapi_types
70
+ {
71
+ :'index' => :'Integer',
72
+ :'sub_type' => :'String',
73
+ :'value' => :'String'
74
+ }
75
+ end
76
+
77
+ # List of attributes with nullable: true
78
+ def self.openapi_nullable
79
+ Set.new([
80
+ ])
81
+ end
82
+
83
+ # Initializes the object
84
+ # @param [Hash] attributes Model attributes in the form of hash
85
+ def initialize(attributes = {})
86
+ if (!attributes.is_a?(Hash))
87
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Zernio::CreateInboxConversationRequestTemplateCardsInnerButtonsInner` initialize method"
88
+ end
89
+
90
+ # check to see if the attribute exists and convert string to symbol for hash key
91
+ acceptable_attribute_map = self.class.acceptable_attribute_map
92
+ attributes = attributes.each_with_object({}) { |(k, v), h|
93
+ if (!acceptable_attribute_map.key?(k.to_sym))
94
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Zernio::CreateInboxConversationRequestTemplateCardsInnerButtonsInner`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
95
+ end
96
+ h[k.to_sym] = v
97
+ }
98
+
99
+ if attributes.key?(:'index')
100
+ self.index = attributes[:'index']
101
+ else
102
+ self.index = nil
103
+ end
104
+
105
+ if attributes.key?(:'sub_type')
106
+ self.sub_type = attributes[:'sub_type']
107
+ else
108
+ self.sub_type = nil
109
+ end
110
+
111
+ if attributes.key?(:'value')
112
+ self.value = attributes[:'value']
113
+ else
114
+ self.value = nil
115
+ end
116
+ end
117
+
118
+ # Show invalid properties with the reasons. Usually used together with valid?
119
+ # @return Array for valid properties with the reasons
120
+ def list_invalid_properties
121
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
122
+ invalid_properties = Array.new
123
+ if @index.nil?
124
+ invalid_properties.push('invalid value for "index", index cannot be nil.')
125
+ end
126
+
127
+ if @index > 1
128
+ invalid_properties.push('invalid value for "index", must be smaller than or equal to 1.')
129
+ end
130
+
131
+ if @index < 0
132
+ invalid_properties.push('invalid value for "index", must be greater than or equal to 0.')
133
+ end
134
+
135
+ if @sub_type.nil?
136
+ invalid_properties.push('invalid value for "sub_type", sub_type cannot be nil.')
137
+ end
138
+
139
+ if @value.nil?
140
+ invalid_properties.push('invalid value for "value", value cannot be nil.')
141
+ end
142
+
143
+ if @value.to_s.length < 1
144
+ invalid_properties.push('invalid value for "value", the character length must be greater than or equal to 1.')
145
+ end
146
+
147
+ invalid_properties
148
+ end
149
+
150
+ # Check to see if the all the properties in the model are valid
151
+ # @return true if the model is valid
152
+ def valid?
153
+ warn '[DEPRECATED] the `valid?` method is obsolete'
154
+ return false if @index.nil?
155
+ return false if @index > 1
156
+ return false if @index < 0
157
+ return false if @sub_type.nil?
158
+ sub_type_validator = EnumAttributeValidator.new('String', ["quick_reply", "url"])
159
+ return false unless sub_type_validator.valid?(@sub_type)
160
+ return false if @value.nil?
161
+ return false if @value.to_s.length < 1
162
+ true
163
+ end
164
+
165
+ # Custom attribute writer method with validation
166
+ # @param [Object] index Value to be assigned
167
+ def index=(index)
168
+ if index.nil?
169
+ fail ArgumentError, 'index cannot be nil'
170
+ end
171
+
172
+ if index > 1
173
+ fail ArgumentError, 'invalid value for "index", must be smaller than or equal to 1.'
174
+ end
175
+
176
+ if index < 0
177
+ fail ArgumentError, 'invalid value for "index", must be greater than or equal to 0.'
178
+ end
179
+
180
+ @index = index
181
+ end
182
+
183
+ # Custom attribute writer method checking allowed values (enum).
184
+ # @param [Object] sub_type Object to be assigned
185
+ def sub_type=(sub_type)
186
+ validator = EnumAttributeValidator.new('String', ["quick_reply", "url"])
187
+ unless validator.valid?(sub_type)
188
+ fail ArgumentError, "invalid value for \"sub_type\", must be one of #{validator.allowable_values}."
189
+ end
190
+ @sub_type = sub_type
191
+ end
192
+
193
+ # Custom attribute writer method with validation
194
+ # @param [Object] value Value to be assigned
195
+ def value=(value)
196
+ if value.nil?
197
+ fail ArgumentError, 'value cannot be nil'
198
+ end
199
+
200
+ if value.to_s.length < 1
201
+ fail ArgumentError, 'invalid value for "value", the character length must be greater than or equal to 1.'
202
+ end
203
+
204
+ @value = value
205
+ end
206
+
207
+ # Checks equality by comparing each attribute.
208
+ # @param [Object] Object to be compared
209
+ def ==(o)
210
+ return true if self.equal?(o)
211
+ self.class == o.class &&
212
+ index == o.index &&
213
+ sub_type == o.sub_type &&
214
+ value == o.value
215
+ end
216
+
217
+ # @see the `==` method
218
+ # @param [Object] Object to be compared
219
+ def eql?(o)
220
+ self == o
221
+ end
222
+
223
+ # Calculates hash code according to all attributes.
224
+ # @return [Integer] Hash code
225
+ def hash
226
+ [index, sub_type, value].hash
227
+ end
228
+
229
+ # Builds the object from hash
230
+ # @param [Hash] attributes Model attributes in the form of hash
231
+ # @return [Object] Returns the model itself
232
+ def self.build_from_hash(attributes)
233
+ return nil unless attributes.is_a?(Hash)
234
+ attributes = attributes.transform_keys(&:to_sym)
235
+ transformed_hash = {}
236
+ openapi_types.each_pair do |key, type|
237
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
238
+ transformed_hash["#{key}"] = nil
239
+ elsif type =~ /\AArray<(.*)>/i
240
+ # check to ensure the input is an array given that the attribute
241
+ # is documented as an array but the input is not
242
+ if attributes[attribute_map[key]].is_a?(Array)
243
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
244
+ end
245
+ elsif !attributes[attribute_map[key]].nil?
246
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
247
+ end
248
+ end
249
+ new(transformed_hash)
250
+ end
251
+
252
+ # Returns the object in the form of hash
253
+ # @return [Hash] Returns the object in the form of hash
254
+ def to_hash
255
+ hash = {}
256
+ self.class.attribute_map.each_pair do |attr, param|
257
+ value = self.send(attr)
258
+ if value.nil?
259
+ is_nullable = self.class.openapi_nullable.include?(attr)
260
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
261
+ end
262
+
263
+ hash[param] = _to_hash(value)
264
+ end
265
+ hash
266
+ end
267
+
268
+ end
269
+
270
+ end
@@ -0,0 +1,210 @@
1
+ =begin
2
+ #Zernio API
3
+
4
+ #API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api Versioning and deprecation: all endpoints are versioned in the URL path (current version: /v1). Breaking changes only ship in a new path version; existing versions keep working. Deprecated operations are marked 'deprecated: true' in this spec and announced in the changelog (https://zernio.com/changelog) before removal. Errors: every 4xx/5xx response is application/json with a machine-readable 'code' and a human-readable 'error' message (see the ErrorResponse schema).
5
+
6
+ The version of the OpenAPI document: 1.0.4
7
+ Contact: support@zernio.com
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.19.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Zernio
17
+ # Overrides this card's header asset for THIS send. Without it, the card's approved sample asset is sent.
18
+ class CreateInboxConversationRequestTemplateCardsInnerHeaderMedia < ApiModelBase
19
+ # Must match the card header's media type.
20
+ attr_accessor :type
21
+
22
+ # Public URL of the asset to send. Must be reachable without auth.
23
+ attr_accessor :link
24
+
25
+ # A Meta media id (from the media upload endpoint), as an alternative to link.
26
+ attr_accessor :id
27
+
28
+ class EnumAttributeValidator
29
+ attr_reader :datatype
30
+ attr_reader :allowable_values
31
+
32
+ def initialize(datatype, allowable_values)
33
+ @allowable_values = allowable_values.map do |value|
34
+ case datatype.to_s
35
+ when /Integer/i
36
+ value.to_i
37
+ when /Float/i
38
+ value.to_f
39
+ else
40
+ value
41
+ end
42
+ end
43
+ end
44
+
45
+ def valid?(value)
46
+ !value || allowable_values.include?(value)
47
+ end
48
+ end
49
+
50
+ # Attribute mapping from ruby-style variable name to JSON key.
51
+ def self.attribute_map
52
+ {
53
+ :'type' => :'type',
54
+ :'link' => :'link',
55
+ :'id' => :'id'
56
+ }
57
+ end
58
+
59
+ # Returns attribute mapping this model knows about
60
+ def self.acceptable_attribute_map
61
+ attribute_map
62
+ end
63
+
64
+ # Returns all the JSON keys this model knows about
65
+ def self.acceptable_attributes
66
+ acceptable_attribute_map.values
67
+ end
68
+
69
+ # Attribute type mapping.
70
+ def self.openapi_types
71
+ {
72
+ :'type' => :'String',
73
+ :'link' => :'String',
74
+ :'id' => :'String'
75
+ }
76
+ end
77
+
78
+ # List of attributes with nullable: true
79
+ def self.openapi_nullable
80
+ Set.new([
81
+ ])
82
+ end
83
+
84
+ # Initializes the object
85
+ # @param [Hash] attributes Model attributes in the form of hash
86
+ def initialize(attributes = {})
87
+ if (!attributes.is_a?(Hash))
88
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Zernio::CreateInboxConversationRequestTemplateCardsInnerHeaderMedia` initialize method"
89
+ end
90
+
91
+ # check to see if the attribute exists and convert string to symbol for hash key
92
+ acceptable_attribute_map = self.class.acceptable_attribute_map
93
+ attributes = attributes.each_with_object({}) { |(k, v), h|
94
+ if (!acceptable_attribute_map.key?(k.to_sym))
95
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Zernio::CreateInboxConversationRequestTemplateCardsInnerHeaderMedia`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
96
+ end
97
+ h[k.to_sym] = v
98
+ }
99
+
100
+ if attributes.key?(:'type')
101
+ self.type = attributes[:'type']
102
+ else
103
+ self.type = nil
104
+ end
105
+
106
+ if attributes.key?(:'link')
107
+ self.link = attributes[:'link']
108
+ end
109
+
110
+ if attributes.key?(:'id')
111
+ self.id = attributes[:'id']
112
+ end
113
+ end
114
+
115
+ # Show invalid properties with the reasons. Usually used together with valid?
116
+ # @return Array for valid properties with the reasons
117
+ def list_invalid_properties
118
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
119
+ invalid_properties = Array.new
120
+ if @type.nil?
121
+ invalid_properties.push('invalid value for "type", type cannot be nil.')
122
+ end
123
+
124
+ invalid_properties
125
+ end
126
+
127
+ # Check to see if the all the properties in the model are valid
128
+ # @return true if the model is valid
129
+ def valid?
130
+ warn '[DEPRECATED] the `valid?` method is obsolete'
131
+ return false if @type.nil?
132
+ type_validator = EnumAttributeValidator.new('String', ["image", "video", "document"])
133
+ return false unless type_validator.valid?(@type)
134
+ true
135
+ end
136
+
137
+ # Custom attribute writer method checking allowed values (enum).
138
+ # @param [Object] type Object to be assigned
139
+ def type=(type)
140
+ validator = EnumAttributeValidator.new('String', ["image", "video", "document"])
141
+ unless validator.valid?(type)
142
+ fail ArgumentError, "invalid value for \"type\", must be one of #{validator.allowable_values}."
143
+ end
144
+ @type = type
145
+ end
146
+
147
+ # Checks equality by comparing each attribute.
148
+ # @param [Object] Object to be compared
149
+ def ==(o)
150
+ return true if self.equal?(o)
151
+ self.class == o.class &&
152
+ type == o.type &&
153
+ link == o.link &&
154
+ id == o.id
155
+ end
156
+
157
+ # @see the `==` method
158
+ # @param [Object] Object to be compared
159
+ def eql?(o)
160
+ self == o
161
+ end
162
+
163
+ # Calculates hash code according to all attributes.
164
+ # @return [Integer] Hash code
165
+ def hash
166
+ [type, link, id].hash
167
+ end
168
+
169
+ # Builds the object from hash
170
+ # @param [Hash] attributes Model attributes in the form of hash
171
+ # @return [Object] Returns the model itself
172
+ def self.build_from_hash(attributes)
173
+ return nil unless attributes.is_a?(Hash)
174
+ attributes = attributes.transform_keys(&:to_sym)
175
+ transformed_hash = {}
176
+ openapi_types.each_pair do |key, type|
177
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
178
+ transformed_hash["#{key}"] = nil
179
+ elsif type =~ /\AArray<(.*)>/i
180
+ # check to ensure the input is an array given that the attribute
181
+ # is documented as an array but the input is not
182
+ if attributes[attribute_map[key]].is_a?(Array)
183
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
184
+ end
185
+ elsif !attributes[attribute_map[key]].nil?
186
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
187
+ end
188
+ end
189
+ new(transformed_hash)
190
+ end
191
+
192
+ # Returns the object in the form of hash
193
+ # @return [Hash] Returns the object in the form of hash
194
+ def to_hash
195
+ hash = {}
196
+ self.class.attribute_map.each_pair do |attr, param|
197
+ value = self.send(attr)
198
+ if value.nil?
199
+ is_nullable = self.class.openapi_nullable.include?(attr)
200
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
201
+ end
202
+
203
+ hash[param] = _to_hash(value)
204
+ end
205
+ hash
206
+ end
207
+
208
+ end
209
+
210
+ end
@@ -11,5 +11,5 @@ Generator version: 7.19.0
11
11
  =end
12
12
 
13
13
  module Zernio
14
- VERSION = '0.0.916'
14
+ VERSION = '0.0.918'
15
15
  end
data/lib/zernio-sdk.rb CHANGED
@@ -339,6 +339,9 @@ require 'zernio-sdk/models/create_inbox_conversation_request'
339
339
  require 'zernio-sdk/models/create_inbox_conversation_request_header_location'
340
340
  require 'zernio-sdk/models/create_inbox_conversation_request_header_media'
341
341
  require 'zernio-sdk/models/create_inbox_conversation_request_template_button_params_inner'
342
+ require 'zernio-sdk/models/create_inbox_conversation_request_template_cards_inner'
343
+ require 'zernio-sdk/models/create_inbox_conversation_request_template_cards_inner_buttons_inner'
344
+ require 'zernio-sdk/models/create_inbox_conversation_request_template_cards_inner_header_media'
342
345
  require 'zernio-sdk/models/create_invite_token201_response'
343
346
  require 'zernio-sdk/models/create_invite_token_request'
344
347
  require 'zernio-sdk/models/create_lead_form200_response'
data/openapi.yaml CHANGED
@@ -28621,6 +28621,7 @@ paths:
28621
28621
  - Templates with media headers (image, video, document) are handled automatically: Zernio reads the approved template definition and fills the header at send time with the template's approved sample asset. To send a DIFFERENT asset per message (e.g. a distinct invoice PDF for each recipient), pass the headerMedia field with a public link (or a Meta media id); it overrides the sample for that send.
28622
28622
  - A template whose approved header format is LOCATION has no header asset to reconstruct at all: Meta only accepts the location at send time, so pass headerLocation (latitude and longitude required) whenever such a template is sent; headerMedia and headerLocation cannot both be supplied.
28623
28623
  - A button that carries its own value at send time (a copy-code button holding a Pix payment code or a coupon, a flow token) is sent with templateButtonParams, addressed by the button's index; templateParams covers text variables and dynamic URL buttons only.
28624
+ - CAROUSEL templates take per-card overrides via templateCards, each addressed by the card's card_index, because card body variables restart at {{1}} per card and cannot be expressed in the flat templateParams order.
28624
28625
  - Template fields are accepted on the JSON body only, not on multipart requests.
28625
28626
 
28626
28627
  For a number you already have a thread with, this sends the template into that thread, which also makes it the way to re-engage a contact after the 24-hour customer-service window has closed. Once the recipient replies (opening the 24h window), send freeform messages with the send-message endpoint (POST /v1/inbox/conversations/{conversationId}/messages).
@@ -28742,6 +28743,65 @@ paths:
28742
28743
  type: string
28743
28744
  minLength: 1
28744
28745
  description: 'The value to send (e.g. the Pix copy-and-paste code for a copy_code button).'
28746
+ templateCards:
28747
+ type: array
28748
+ maxItems: 10
28749
+ description: >-
28750
+ WhatsApp only. Per-card overrides for a CAROUSEL template, each addressed by
28751
+ the card's card_index. Carousel card body variables restart at {{1}} per card,
28752
+ so they cannot be expressed in the flat templateParams slot order; use this
28753
+ instead. A cardIndex naming a card the approved template does not have, a
28754
+ duplicate cardIndex, or a params count that does not match the card body's
28755
+ token count is rejected with 400 (INVALID_TEMPLATE_CARD_PARAM).
28756
+ items:
28757
+ type: object
28758
+ required: [cardIndex]
28759
+ properties:
28760
+ cardIndex:
28761
+ type: integer
28762
+ minimum: 0
28763
+ maximum: 9
28764
+ description: 'The card''s card_index in the approved template.'
28765
+ params:
28766
+ type: array
28767
+ items: { type: string }
28768
+ description: 'Values for this card''s own body variables, in the card''s own {{1}}, {{2}}, ... order (or named-slot order of appearance).'
28769
+ headerMedia:
28770
+ type: object
28771
+ description: 'Overrides this card''s header asset for THIS send. Without it, the card''s approved sample asset is sent.'
28772
+ required: [type]
28773
+ properties:
28774
+ type:
28775
+ type: string
28776
+ enum: [image, video, document]
28777
+ description: Must match the card header's media type.
28778
+ link:
28779
+ type: string
28780
+ description: Public URL of the asset to send. Must be reachable without auth.
28781
+ id:
28782
+ type: string
28783
+ description: A Meta media id (from the media upload endpoint), as an alternative to link.
28784
+ buttons:
28785
+ type: array
28786
+ maxItems: 2
28787
+ description: "Values for this card's own buttons, each addressed by the button's index within the card."
28788
+ items:
28789
+ type: object
28790
+ required: [index, subType, value]
28791
+ properties:
28792
+ index:
28793
+ type: integer
28794
+ minimum: 0
28795
+ maximum: 1
28796
+ description: 'Zero-based position of the button within the card''s buttons.'
28797
+ subType:
28798
+ type: string
28799
+ enum: [quick_reply, url]
28800
+ description: The button kind, which decides how the value is sent.
28801
+ value:
28802
+ type: string
28803
+ minLength: 1
28804
+ description: 'The value to send (quick_reply payload, or the URL dynamic suffix).'
28745
28805
  headerMedia:
28746
28806
  type: object
28747
28807
  description: >-
@@ -28839,14 +28899,14 @@ paths:
28839
28899
  type: [string, "null"]
28840
28900
  description: X username of the recipient
28841
28901
  '400':
28842
- description: 'Validation error, platform not supported, an attachment the platform does not accept (PLATFORM_LIMITATION), template required to start a WhatsApp conversation (TEMPLATE_REQUIRED), template variables that do not match the approved definition (INVALID_TEMPLATE_PARAMS, INVALID_TEMPLATE_BUTTON_PARAM), category combined with templateName or used on a non-WhatsApp account, or the WhatsApp Business Account is not eligible for Direct Send: DIRECT_SEND_NOT_ELIGIBLE and DIRECT_SEND_BLOCKED require Meta to grant or restore Direct Send access, while DIRECT_SEND_LIMITED is temporary and lifts on its own'
28902
+ description: 'Validation error, platform not supported, an attachment the platform does not accept (PLATFORM_LIMITATION), template required to start a WhatsApp conversation (TEMPLATE_REQUIRED), template variables that do not match the approved definition (INVALID_TEMPLATE_PARAMS, INVALID_TEMPLATE_BUTTON_PARAM), templateCards that do not match the approved carousel definition (INVALID_TEMPLATE_CARD_PARAM), category combined with templateName or used on a non-WhatsApp account, or the WhatsApp Business Account is not eligible for Direct Send: DIRECT_SEND_NOT_ELIGIBLE and DIRECT_SEND_BLOCKED require Meta to grant or restore Direct Send access, while DIRECT_SEND_LIMITED is temporary and lifts on its own'
28843
28903
  content:
28844
28904
  application/json:
28845
28905
  schema:
28846
28906
  type: object
28847
28907
  properties:
28848
28908
  error: { type: string }
28849
- code: { type: string, enum: [PLATFORM_NOT_SUPPORTED, PLATFORM_LIMITATION, TEMPLATE_REQUIRED, INVALID_TEMPLATE_PARAMS, INVALID_TEMPLATE_BUTTON_PARAM, DIRECT_SEND_NOT_ELIGIBLE, DIRECT_SEND_LIMITED, DIRECT_SEND_BLOCKED] }
28909
+ code: { type: string, enum: [PLATFORM_NOT_SUPPORTED, PLATFORM_LIMITATION, TEMPLATE_REQUIRED, INVALID_TEMPLATE_PARAMS, INVALID_TEMPLATE_BUTTON_PARAM, INVALID_TEMPLATE_CARD_PARAM, DIRECT_SEND_NOT_ELIGIBLE, DIRECT_SEND_LIMITED, DIRECT_SEND_BLOCKED] }
28850
28910
  '401': { $ref: '#/components/responses/Unauthorized' }
28851
28911
  '403':
28852
28912
  description: Inbox addon required or profile limit reached
@@ -50857,6 +50917,7 @@ paths:
50857
50917
  request with Google without creating or persisting resources. Read assets with
50858
50918
  `GET /v1/ads/campaigns/{campaignId}/asset-groups`. The logo is required; video is
50859
50919
  optional via `assetGroup.youtubeVideoId`. Brand guidelines are disabled at creation.
50920
+ All supplied asset links are validated together against Google's minimum asset requirements.
50860
50921
  PMax rejects ACTIVE creation, portfolio bidding, bid caps, legacy creative fields
50861
50922
  and attach shapes. Geo and language targeting are supported; omitted geo targets
50862
50923
  all locations. PMax does not require top-level goal, headline, body or linkUrl.
@@ -135,7 +135,7 @@ describe 'AdCampaignsApi' do
135
135
 
136
136
  # unit tests for create_standalone_ad
137
137
  # Create standalone ad
138
- # Create a paid ad with custom creative across Meta, Google Ads, Pinterest, TikTok, X, LinkedIn, and OpenAI Ads (ChatGPT Ads). Google Performance Max: set &#x60;campaignType: \&quot;pmax\&quot;&#x60; and supply &#x60;assetGroup&#x60; with text, images by role, business name and finalUrl. Creates a daily budget, PAUSED campaign and asset group atomically. &#x60;validateOnly: true&#x60; validates the complete request with Google without creating or persisting resources. Read assets with &#x60;GET /v1/ads/campaigns/{campaignId}/asset-groups&#x60;. The logo is required; video is optional via &#x60;assetGroup.youtubeVideoId&#x60;. Brand guidelines are disabled at creation. PMax rejects ACTIVE creation, portfolio bidding, bid caps, legacy creative fields and attach shapes. Geo and language targeting are supported; omitted geo targets all locations. PMax does not require top-level goal, headline, body or linkUrl. Supported bidding: omitted or LOWEST_COST_WITHOUT_CAP for Maximize Conversions, COST_CAP plus bidAmount for target CPA, LOWEST_COST_WITH_MIN_ROAS plus roasAverageFloor for Maximize Conversion Value with target ROAS. Other mutually-exclusive request shapes are selected by the body: - Legacy single-creative shape (all platforms, the default). - Meta-only multi-creative shape via the creatives array: one ad set with N ads sharing budget and targeting. - Attach shape via adSetId: adds one new ad to an existing ad set, inheriting its budget, targeting, and schedule (Meta, Google Ads, TikTok, and LinkedIn). On LinkedIn adSetId is the existing Campaign id, and the budget, schedule, targeting and bidding fields must be omitted. Meta accepts &#x60;promotion&#x60; and &#x60;creativeFeatures&#x60; on the single and attach shapes and as defaults for &#x60;creatives[]&#x60;. An item replaces the whole feature map; its &#x60;promotion&#x60; replaces the default offer, and &#x60;promotion: null&#x60; disables that default for the item. Reusing &#x60;existingCreativeId&#x60; uses the existing creative settings instead of new settings. Requested settings are persisted for lists, exports, and default ad-detail reads. Only ads supplied a &#x60;promotion&#x60; receive live readback; multi-create batches those reads in groups of up to 50 IDs without per-ad fallback. Inspect &#x60;ad.creative.promotionStatus&#x60; (or &#x60;ads[].creative.promotionStatus&#x60;). &#x60;not_returned&#x60; means Meta omitted the metadata; successful creation does not by itself prove the offer was applied or will display. Per-platform required fields, budget minimums, and video-ad rules are documented on each property below. LinkedIn creates a Single Image or Single Video Ad backed by a Direct Sponsored Content \&quot;dark post\&quot; authored by a Company Page (see &#x60;organizationId&#x60;). Supported goals are engagement, traffic, awareness, and video_views (video ads use the &#x60;video&#x60; field; video_views requires a video), and traffic ads require &#x60;linkUrl&#x60;. **Idempotency:** this endpoint is not idempotent at the platform level (a blind retry creates a second campaign/ad set/ad). Send an &#x60;Idempotency-Key&#x60; header to make retries safe: the first request with a given key creates the ad and we store the response; a retry with the same key replays that exact response (with &#x60;Idempotent-Replayed: true&#x60;) instead of creating duplicates. Reusing a key with a different body returns 422; a key whose first request is still in flight returns 409 (retry after a short backoff). Keys are scoped to your credential and expire after 24h.
138
+ # Create a paid ad with custom creative across Meta, Google Ads, Pinterest, TikTok, X, LinkedIn, and OpenAI Ads (ChatGPT Ads). Google Performance Max: set &#x60;campaignType: \&quot;pmax\&quot;&#x60; and supply &#x60;assetGroup&#x60; with text, images by role, business name and finalUrl. Creates a daily budget, PAUSED campaign and asset group atomically. &#x60;validateOnly: true&#x60; validates the complete request with Google without creating or persisting resources. Read assets with &#x60;GET /v1/ads/campaigns/{campaignId}/asset-groups&#x60;. The logo is required; video is optional via &#x60;assetGroup.youtubeVideoId&#x60;. Brand guidelines are disabled at creation. All supplied asset links are validated together against Google&#39;s minimum asset requirements. PMax rejects ACTIVE creation, portfolio bidding, bid caps, legacy creative fields and attach shapes. Geo and language targeting are supported; omitted geo targets all locations. PMax does not require top-level goal, headline, body or linkUrl. Supported bidding: omitted or LOWEST_COST_WITHOUT_CAP for Maximize Conversions, COST_CAP plus bidAmount for target CPA, LOWEST_COST_WITH_MIN_ROAS plus roasAverageFloor for Maximize Conversion Value with target ROAS. Other mutually-exclusive request shapes are selected by the body: - Legacy single-creative shape (all platforms, the default). - Meta-only multi-creative shape via the creatives array: one ad set with N ads sharing budget and targeting. - Attach shape via adSetId: adds one new ad to an existing ad set, inheriting its budget, targeting, and schedule (Meta, Google Ads, TikTok, and LinkedIn). On LinkedIn adSetId is the existing Campaign id, and the budget, schedule, targeting and bidding fields must be omitted. Meta accepts &#x60;promotion&#x60; and &#x60;creativeFeatures&#x60; on the single and attach shapes and as defaults for &#x60;creatives[]&#x60;. An item replaces the whole feature map; its &#x60;promotion&#x60; replaces the default offer, and &#x60;promotion: null&#x60; disables that default for the item. Reusing &#x60;existingCreativeId&#x60; uses the existing creative settings instead of new settings. Requested settings are persisted for lists, exports, and default ad-detail reads. Only ads supplied a &#x60;promotion&#x60; receive live readback; multi-create batches those reads in groups of up to 50 IDs without per-ad fallback. Inspect &#x60;ad.creative.promotionStatus&#x60; (or &#x60;ads[].creative.promotionStatus&#x60;). &#x60;not_returned&#x60; means Meta omitted the metadata; successful creation does not by itself prove the offer was applied or will display. Per-platform required fields, budget minimums, and video-ad rules are documented on each property below. LinkedIn creates a Single Image or Single Video Ad backed by a Direct Sponsored Content \&quot;dark post\&quot; authored by a Company Page (see &#x60;organizationId&#x60;). Supported goals are engagement, traffic, awareness, and video_views (video ads use the &#x60;video&#x60; field; video_views requires a video), and traffic ads require &#x60;linkUrl&#x60;. **Idempotency:** this endpoint is not idempotent at the platform level (a blind retry creates a second campaign/ad set/ad). Send an &#x60;Idempotency-Key&#x60; header to make retries safe: the first request with a given key creates the ad and we store the response; a retry with the same key replays that exact response (with &#x60;Idempotent-Replayed: true&#x60;) instead of creating duplicates. Reusing a key with a different body returns 422; a key whose first request is still in flight returns 409 (retry after a short backoff). Keys are scoped to your credential and expire after 24h.
139
139
  # @param create_standalone_ad_request
140
140
  # @param [Hash] opts the optional parameters
141
141
  # @option opts [String] :idempotency_key Optional client-generated unique key (e.g. a UUID) that makes retries safe. Same key + same body replays the original response; same key + different body → 422; key still processing → 409.
@@ -48,7 +48,7 @@ describe 'MessagesApi' do
48
48
 
49
49
  # unit tests for create_inbox_conversation
50
50
  # Create conversation
51
- # Start a direct message conversation with a user. If a conversation with that recipient already exists, the message is added to the existing thread. Supported platforms: X, Bluesky, Reddit, WhatsApp, SMS, and Slack. Other platforms return PLATFORM_NOT_SUPPORTED. **Slack.** Pass a workspace member id as participantId (list them with GET /v1/accounts/{accountId}/slack-members). Zernio opens the DM channel with that member and sends the message; the thread then behaves like any other Slack conversation in the inbox. The member must belong to the connected workspace. **WhatsApp.** This is the endpoint for sending an approved template message to a phone number. Provide templateName, templateLanguage, and templateParams (variable values for the text header, body and dynamic URL buttons, in that order), with the recipient phone in participantId. A template is required because WhatsApp does not permit freeform messages to open a conversation; a missing template returns TEMPLATE_REQUIRED. - Templates with media headers (image, video, document) are handled automatically: Zernio reads the approved template definition and fills the header at send time with the template&#39;s approved sample asset. To send a DIFFERENT asset per message (e.g. a distinct invoice PDF for each recipient), pass the headerMedia field with a public link (or a Meta media id); it overrides the sample for that send. - A template whose approved header format is LOCATION has no header asset to reconstruct at all: Meta only accepts the location at send time, so pass headerLocation (latitude and longitude required) whenever such a template is sent; headerMedia and headerLocation cannot both be supplied. - A button that carries its own value at send time (a copy-code button holding a Pix payment code or a coupon, a flow token) is sent with templateButtonParams, addressed by the button&#39;s index; templateParams covers text variables and dynamic URL buttons only. - Template fields are accepted on the JSON body only, not on multipart requests. For a number you already have a thread with, this sends the template into that thread, which also makes it the way to re-engage a contact after the 24-hour customer-service window has closed. Once the recipient replies (opening the 24h window), send freeform messages with the send-message endpoint (POST /v1/inbox/conversations/{conversationId}/messages). Alternatively, WhatsApp Business Accounts eligible for Meta Direct Send can open a conversation with a business-initiated utility text message and no template: pass category: &#39;utility&#39; together with message (and no templateName). See the category field below. **DM eligibility (X).** Before sending, the endpoint checks if the recipient accepts DMs from your account (via the receives_your_dm field). If not, a 422 error with code DM_NOT_ALLOWED is returned. You can skip this check with skipDmCheck: true if you have already verified eligibility. **X API tier requirement.** DM write endpoints require X API Pro tier ($5,000/month) or Enterprise access. This applies to BYOK (Bring Your Own Key) users who provide their own X API credentials. **Rate limits (X only).** X&#39;s DM API enforces 200 requests per 15 minutes, 1,000 per 24 hours per connected X account, and 15,000 per 24 hours per X developer app (shared across all DM endpoints). These limits do NOT apply to other platforms. WhatsApp sends are governed by Meta&#39;s per-number messaging tiers (unique business-initiated conversations per 24 hours) and per-number throughput instead.
51
+ # Start a direct message conversation with a user. If a conversation with that recipient already exists, the message is added to the existing thread. Supported platforms: X, Bluesky, Reddit, WhatsApp, SMS, and Slack. Other platforms return PLATFORM_NOT_SUPPORTED. **Slack.** Pass a workspace member id as participantId (list them with GET /v1/accounts/{accountId}/slack-members). Zernio opens the DM channel with that member and sends the message; the thread then behaves like any other Slack conversation in the inbox. The member must belong to the connected workspace. **WhatsApp.** This is the endpoint for sending an approved template message to a phone number. Provide templateName, templateLanguage, and templateParams (variable values for the text header, body and dynamic URL buttons, in that order), with the recipient phone in participantId. A template is required because WhatsApp does not permit freeform messages to open a conversation; a missing template returns TEMPLATE_REQUIRED. - Templates with media headers (image, video, document) are handled automatically: Zernio reads the approved template definition and fills the header at send time with the template&#39;s approved sample asset. To send a DIFFERENT asset per message (e.g. a distinct invoice PDF for each recipient), pass the headerMedia field with a public link (or a Meta media id); it overrides the sample for that send. - A template whose approved header format is LOCATION has no header asset to reconstruct at all: Meta only accepts the location at send time, so pass headerLocation (latitude and longitude required) whenever such a template is sent; headerMedia and headerLocation cannot both be supplied. - A button that carries its own value at send time (a copy-code button holding a Pix payment code or a coupon, a flow token) is sent with templateButtonParams, addressed by the button&#39;s index; templateParams covers text variables and dynamic URL buttons only. - CAROUSEL templates take per-card overrides via templateCards, each addressed by the card&#39;s card_index, because card body variables restart at {{1}} per card and cannot be expressed in the flat templateParams order. - Template fields are accepted on the JSON body only, not on multipart requests. For a number you already have a thread with, this sends the template into that thread, which also makes it the way to re-engage a contact after the 24-hour customer-service window has closed. Once the recipient replies (opening the 24h window), send freeform messages with the send-message endpoint (POST /v1/inbox/conversations/{conversationId}/messages). Alternatively, WhatsApp Business Accounts eligible for Meta Direct Send can open a conversation with a business-initiated utility text message and no template: pass category: &#39;utility&#39; together with message (and no templateName). See the category field below. **DM eligibility (X).** Before sending, the endpoint checks if the recipient accepts DMs from your account (via the receives_your_dm field). If not, a 422 error with code DM_NOT_ALLOWED is returned. You can skip this check with skipDmCheck: true if you have already verified eligibility. **X API tier requirement.** DM write endpoints require X API Pro tier ($5,000/month) or Enterprise access. This applies to BYOK (Bring Your Own Key) users who provide their own X API credentials. **Rate limits (X only).** X&#39;s DM API enforces 200 requests per 15 minutes, 1,000 per 24 hours per connected X account, and 15,000 per 24 hours per X developer app (shared across all DM endpoints). These limits do NOT apply to other platforms. WhatsApp sends are governed by Meta&#39;s per-number messaging tiers (unique business-initiated conversations per 24 hours) and per-number throughput instead.
52
52
  # @param create_inbox_conversation_request
53
53
  # @param [Hash] opts the optional parameters
54
54
  # @return [CreateInboxConversation201Response]
@@ -36,7 +36,7 @@ describe Zernio::CreateInboxConversation400Response do
36
36
  describe 'test attribute "code"' do
37
37
  it 'should work' do
38
38
  # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
39
- # validator = Petstore::EnumTest::EnumAttributeValidator.new('String', ["PLATFORM_NOT_SUPPORTED", "PLATFORM_LIMITATION", "TEMPLATE_REQUIRED", "INVALID_TEMPLATE_PARAMS", "INVALID_TEMPLATE_BUTTON_PARAM", "DIRECT_SEND_NOT_ELIGIBLE", "DIRECT_SEND_LIMITED", "DIRECT_SEND_BLOCKED"])
39
+ # validator = Petstore::EnumTest::EnumAttributeValidator.new('String', ["PLATFORM_NOT_SUPPORTED", "PLATFORM_LIMITATION", "TEMPLATE_REQUIRED", "INVALID_TEMPLATE_PARAMS", "INVALID_TEMPLATE_BUTTON_PARAM", "INVALID_TEMPLATE_CARD_PARAM", "DIRECT_SEND_NOT_ELIGIBLE", "DIRECT_SEND_LIMITED", "DIRECT_SEND_BLOCKED"])
40
40
  # validator.allowable_values.each do |value|
41
41
  # expect { instance.code = value }.not_to raise_error
42
42
  # end
@@ -97,6 +97,12 @@ describe Zernio::CreateInboxConversationRequest do
97
97
  end
98
98
  end
99
99
 
100
+ describe 'test attribute "template_cards"' do
101
+ it 'should work' do
102
+ # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
103
+ end
104
+ end
105
+
100
106
  describe 'test attribute "header_media"' do
101
107
  it 'should work' do
102
108
  # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/