late-sdk 0.0.690 → 0.0.692

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/docs/AdCampaignsApi.md +2 -2
  4. data/docs/MoveWhatsAppNumberToProfile200Response.md +22 -0
  5. data/docs/MoveWhatsAppNumberToProfileRequest.md +18 -0
  6. data/docs/UpdateAdCampaign200Response.md +5 -1
  7. data/docs/UpdateAdCampaignRequest.md +10 -6
  8. data/docs/UpdateAdCampaignRequestPlatformSpecificData.md +1 -1
  9. data/docs/WhatsAppPhoneNumbersApi.md +72 -0
  10. data/lib/zernio-sdk/api/ad_campaigns_api.rb +4 -4
  11. data/lib/zernio-sdk/api/whats_app_phone_numbers_api.rb +74 -0
  12. data/lib/zernio-sdk/models/move_whats_app_number_to_profile200_response.rb +169 -0
  13. data/lib/zernio-sdk/models/move_whats_app_number_to_profile_request.rb +165 -0
  14. data/lib/zernio-sdk/models/update_ad_campaign200_response.rb +21 -1
  15. data/lib/zernio-sdk/models/update_ad_campaign_request.rb +42 -21
  16. data/lib/zernio-sdk/models/update_ad_campaign_request_budget.rb +1 -0
  17. data/lib/zernio-sdk/models/update_ad_campaign_request_platform_specific_data.rb +2 -2
  18. data/lib/zernio-sdk/version.rb +1 -1
  19. data/lib/zernio-sdk.rb +2 -0
  20. data/openapi.yaml +96 -33
  21. data/spec/api/ad_campaigns_api_spec.rb +2 -2
  22. data/spec/api/whats_app_phone_numbers_api_spec.rb +13 -0
  23. data/spec/models/move_whats_app_number_to_profile200_response_spec.rb +48 -0
  24. data/spec/models/move_whats_app_number_to_profile_request_spec.rb +36 -0
  25. data/spec/models/update_ad_campaign200_response_spec.rb +12 -0
  26. data/spec/models/update_ad_campaign_request_spec.rb +19 -7
  27. data/zernio-sdk-0.0.692.gem +0 -0
  28. metadata +10 -2
  29. data/zernio-sdk-0.0.690.gem +0 -0
@@ -0,0 +1,165 @@
1
+ =begin
2
+ #Zernio API
3
+
4
+ #API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api
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 MoveWhatsAppNumberToProfileRequest < ApiModelBase
18
+ # Destination profile id. Must belong to the same team.
19
+ attr_accessor :profile_id
20
+
21
+ # Attribute mapping from ruby-style variable name to JSON key.
22
+ def self.attribute_map
23
+ {
24
+ :'profile_id' => :'profileId'
25
+ }
26
+ end
27
+
28
+ # Returns attribute mapping this model knows about
29
+ def self.acceptable_attribute_map
30
+ attribute_map
31
+ end
32
+
33
+ # Returns all the JSON keys this model knows about
34
+ def self.acceptable_attributes
35
+ acceptable_attribute_map.values
36
+ end
37
+
38
+ # Attribute type mapping.
39
+ def self.openapi_types
40
+ {
41
+ :'profile_id' => :'String'
42
+ }
43
+ end
44
+
45
+ # List of attributes with nullable: true
46
+ def self.openapi_nullable
47
+ Set.new([
48
+ ])
49
+ end
50
+
51
+ # Initializes the object
52
+ # @param [Hash] attributes Model attributes in the form of hash
53
+ def initialize(attributes = {})
54
+ if (!attributes.is_a?(Hash))
55
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Zernio::MoveWhatsAppNumberToProfileRequest` initialize method"
56
+ end
57
+
58
+ # check to see if the attribute exists and convert string to symbol for hash key
59
+ acceptable_attribute_map = self.class.acceptable_attribute_map
60
+ attributes = attributes.each_with_object({}) { |(k, v), h|
61
+ if (!acceptable_attribute_map.key?(k.to_sym))
62
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Zernio::MoveWhatsAppNumberToProfileRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
63
+ end
64
+ h[k.to_sym] = v
65
+ }
66
+
67
+ if attributes.key?(:'profile_id')
68
+ self.profile_id = attributes[:'profile_id']
69
+ else
70
+ self.profile_id = nil
71
+ end
72
+ end
73
+
74
+ # Show invalid properties with the reasons. Usually used together with valid?
75
+ # @return Array for valid properties with the reasons
76
+ def list_invalid_properties
77
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
78
+ invalid_properties = Array.new
79
+ if @profile_id.nil?
80
+ invalid_properties.push('invalid value for "profile_id", profile_id cannot be nil.')
81
+ end
82
+
83
+ invalid_properties
84
+ end
85
+
86
+ # Check to see if the all the properties in the model are valid
87
+ # @return true if the model is valid
88
+ def valid?
89
+ warn '[DEPRECATED] the `valid?` method is obsolete'
90
+ return false if @profile_id.nil?
91
+ true
92
+ end
93
+
94
+ # Custom attribute writer method with validation
95
+ # @param [Object] profile_id Value to be assigned
96
+ def profile_id=(profile_id)
97
+ if profile_id.nil?
98
+ fail ArgumentError, 'profile_id cannot be nil'
99
+ end
100
+
101
+ @profile_id = profile_id
102
+ end
103
+
104
+ # Checks equality by comparing each attribute.
105
+ # @param [Object] Object to be compared
106
+ def ==(o)
107
+ return true if self.equal?(o)
108
+ self.class == o.class &&
109
+ profile_id == o.profile_id
110
+ end
111
+
112
+ # @see the `==` method
113
+ # @param [Object] Object to be compared
114
+ def eql?(o)
115
+ self == o
116
+ end
117
+
118
+ # Calculates hash code according to all attributes.
119
+ # @return [Integer] Hash code
120
+ def hash
121
+ [profile_id].hash
122
+ end
123
+
124
+ # Builds the object from hash
125
+ # @param [Hash] attributes Model attributes in the form of hash
126
+ # @return [Object] Returns the model itself
127
+ def self.build_from_hash(attributes)
128
+ return nil unless attributes.is_a?(Hash)
129
+ attributes = attributes.transform_keys(&:to_sym)
130
+ transformed_hash = {}
131
+ openapi_types.each_pair do |key, type|
132
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
133
+ transformed_hash["#{key}"] = nil
134
+ elsif type =~ /\AArray<(.*)>/i
135
+ # check to ensure the input is an array given that the attribute
136
+ # is documented as an array but the input is not
137
+ if attributes[attribute_map[key]].is_a?(Array)
138
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
139
+ end
140
+ elsif !attributes[attribute_map[key]].nil?
141
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
142
+ end
143
+ end
144
+ new(transformed_hash)
145
+ end
146
+
147
+ # Returns the object in the form of hash
148
+ # @return [Hash] Returns the object in the form of hash
149
+ def to_hash
150
+ hash = {}
151
+ self.class.attribute_map.each_pair do |attr, param|
152
+ value = self.send(attr)
153
+ if value.nil?
154
+ is_nullable = self.class.openapi_nullable.include?(attr)
155
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
156
+ end
157
+
158
+ hash[param] = _to_hash(value)
159
+ end
160
+ hash
161
+ end
162
+
163
+ end
164
+
165
+ end
@@ -14,7 +14,9 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Zernio
17
+ # Echoes back only the fields you sent, plus `updated`.
17
18
  class UpdateAdCampaign200Response < ApiModelBase
19
+ # Local Ad documents mirrored. 0 on the empty-campaign path.
18
20
  attr_accessor :updated
19
21
 
20
22
  attr_accessor :budget
@@ -23,6 +25,10 @@ module Zernio
23
25
 
24
26
  attr_accessor :bid_strategy
25
27
 
28
+ attr_accessor :bid_amount
29
+
30
+ attr_accessor :roas_average_floor
31
+
26
32
  attr_accessor :platform_specific_data
27
33
 
28
34
  class EnumAttributeValidator
@@ -54,6 +60,8 @@ module Zernio
54
60
  :'budget' => :'budget',
55
61
  :'budget_level' => :'budgetLevel',
56
62
  :'bid_strategy' => :'bidStrategy',
63
+ :'bid_amount' => :'bidAmount',
64
+ :'roas_average_floor' => :'roasAverageFloor',
57
65
  :'platform_specific_data' => :'platformSpecificData'
58
66
  }
59
67
  end
@@ -75,6 +83,8 @@ module Zernio
75
83
  :'budget' => :'AdBudget',
76
84
  :'budget_level' => :'String',
77
85
  :'bid_strategy' => :'BidStrategy',
86
+ :'bid_amount' => :'Float',
87
+ :'roas_average_floor' => :'Float',
78
88
  :'platform_specific_data' => :'Object'
79
89
  }
80
90
  end
@@ -117,6 +127,14 @@ module Zernio
117
127
  self.bid_strategy = attributes[:'bid_strategy']
118
128
  end
119
129
 
130
+ if attributes.key?(:'bid_amount')
131
+ self.bid_amount = attributes[:'bid_amount']
132
+ end
133
+
134
+ if attributes.key?(:'roas_average_floor')
135
+ self.roas_average_floor = attributes[:'roas_average_floor']
136
+ end
137
+
120
138
  if attributes.key?(:'platform_specific_data')
121
139
  self.platform_specific_data = attributes[:'platform_specific_data']
122
140
  end
@@ -158,6 +176,8 @@ module Zernio
158
176
  budget == o.budget &&
159
177
  budget_level == o.budget_level &&
160
178
  bid_strategy == o.bid_strategy &&
179
+ bid_amount == o.bid_amount &&
180
+ roas_average_floor == o.roas_average_floor &&
161
181
  platform_specific_data == o.platform_specific_data
162
182
  end
163
183
 
@@ -170,7 +190,7 @@ module Zernio
170
190
  # Calculates hash code according to all attributes.
171
191
  # @return [Integer] Hash code
172
192
  def hash
173
- [updated, budget, budget_level, bid_strategy, platform_specific_data].hash
193
+ [updated, budget, budget_level, bid_strategy, bid_amount, roas_average_floor, platform_specific_data].hash
174
194
  end
175
195
 
176
196
  # Builds the object from hash
@@ -15,17 +15,24 @@ require 'time'
15
15
 
16
16
  module Zernio
17
17
  class UpdateAdCampaignRequest < ApiModelBase
18
- # Zernio SocialAccount id owning the ad account. Required only to update an EMPTY campaign (zero ads), which has no local Ad documents to resolve a token from.
19
- attr_accessor :account_id
20
-
18
+ # Required: platform campaign IDs are not globally unique.
21
19
  attr_accessor :platform
22
20
 
23
- attr_accessor :budget
21
+ # **Meta only.** Zernio SocialAccount id owning the ad account. Needed only for an EMPTY campaign (zero ads); ignored otherwise.
22
+ attr_accessor :account_id
24
23
 
25
- # Campaign-level default. Ad sets inherit this unless they override.
24
+ # **Meta + Google.** On Meta, the campaign default that ad sets inherit unless they override it. On Google, the campaign's own bidding strategy.
26
25
  attr_accessor :bid_strategy
27
26
 
28
- # Rename the campaign (Meta only; other platforms return 501). At least one of budget/bidStrategy/name/platformSpecificData is required.
27
+ # **Google only.** Whole currency units (USD: 12 = $12.00). Max CPC for LOWEST_COST_WITH_BID_CAP, CPA target for COST_CAP; required for both.
28
+ attr_accessor :bid_amount
29
+
30
+ # **Google only.** Decimal ROAS multiplier (2.0 = 2.0x), required for LOWEST_COST_WITH_MIN_ROAS.
31
+ attr_accessor :roas_average_floor
32
+
33
+ attr_accessor :budget
34
+
35
+ # **Meta only.** Rename the campaign.
29
36
  attr_accessor :name
30
37
 
31
38
  attr_accessor :platform_specific_data
@@ -55,10 +62,12 @@ module Zernio
55
62
  # Attribute mapping from ruby-style variable name to JSON key.
56
63
  def self.attribute_map
57
64
  {
58
- :'account_id' => :'accountId',
59
65
  :'platform' => :'platform',
60
- :'budget' => :'budget',
66
+ :'account_id' => :'accountId',
61
67
  :'bid_strategy' => :'bidStrategy',
68
+ :'bid_amount' => :'bidAmount',
69
+ :'roas_average_floor' => :'roasAverageFloor',
70
+ :'budget' => :'budget',
62
71
  :'name' => :'name',
63
72
  :'platform_specific_data' => :'platformSpecificData'
64
73
  }
@@ -77,10 +86,12 @@ module Zernio
77
86
  # Attribute type mapping.
78
87
  def self.openapi_types
79
88
  {
80
- :'account_id' => :'String',
81
89
  :'platform' => :'String',
82
- :'budget' => :'UpdateAdCampaignRequestBudget',
90
+ :'account_id' => :'String',
83
91
  :'bid_strategy' => :'BidStrategy',
92
+ :'bid_amount' => :'Float',
93
+ :'roas_average_floor' => :'Float',
94
+ :'budget' => :'UpdateAdCampaignRequestBudget',
84
95
  :'name' => :'String',
85
96
  :'platform_specific_data' => :'UpdateAdCampaignRequestPlatformSpecificData'
86
97
  }
@@ -108,24 +119,32 @@ module Zernio
108
119
  h[k.to_sym] = v
109
120
  }
110
121
 
111
- if attributes.key?(:'account_id')
112
- self.account_id = attributes[:'account_id']
113
- end
114
-
115
122
  if attributes.key?(:'platform')
116
123
  self.platform = attributes[:'platform']
117
124
  else
118
125
  self.platform = nil
119
126
  end
120
127
 
121
- if attributes.key?(:'budget')
122
- self.budget = attributes[:'budget']
128
+ if attributes.key?(:'account_id')
129
+ self.account_id = attributes[:'account_id']
123
130
  end
124
131
 
125
132
  if attributes.key?(:'bid_strategy')
126
133
  self.bid_strategy = attributes[:'bid_strategy']
127
134
  end
128
135
 
136
+ if attributes.key?(:'bid_amount')
137
+ self.bid_amount = attributes[:'bid_amount']
138
+ end
139
+
140
+ if attributes.key?(:'roas_average_floor')
141
+ self.roas_average_floor = attributes[:'roas_average_floor']
142
+ end
143
+
144
+ if attributes.key?(:'budget')
145
+ self.budget = attributes[:'budget']
146
+ end
147
+
129
148
  if attributes.key?(:'name')
130
149
  self.name = attributes[:'name']
131
150
  end
@@ -156,7 +175,7 @@ module Zernio
156
175
  def valid?
157
176
  warn '[DEPRECATED] the `valid?` method is obsolete'
158
177
  return false if @platform.nil?
159
- platform_validator = EnumAttributeValidator.new('String', ["facebook", "instagram"])
178
+ platform_validator = EnumAttributeValidator.new('String', ["facebook", "instagram", "google"])
160
179
  return false unless platform_validator.valid?(@platform)
161
180
  return false if !@name.nil? && @name.to_s.length > 255
162
181
  true
@@ -165,7 +184,7 @@ module Zernio
165
184
  # Custom attribute writer method checking allowed values (enum).
166
185
  # @param [Object] platform Object to be assigned
167
186
  def platform=(platform)
168
- validator = EnumAttributeValidator.new('String', ["facebook", "instagram"])
187
+ validator = EnumAttributeValidator.new('String', ["facebook", "instagram", "google"])
169
188
  unless validator.valid?(platform)
170
189
  fail ArgumentError, "invalid value for \"platform\", must be one of #{validator.allowable_values}."
171
190
  end
@@ -191,10 +210,12 @@ module Zernio
191
210
  def ==(o)
192
211
  return true if self.equal?(o)
193
212
  self.class == o.class &&
194
- account_id == o.account_id &&
195
213
  platform == o.platform &&
196
- budget == o.budget &&
214
+ account_id == o.account_id &&
197
215
  bid_strategy == o.bid_strategy &&
216
+ bid_amount == o.bid_amount &&
217
+ roas_average_floor == o.roas_average_floor &&
218
+ budget == o.budget &&
198
219
  name == o.name &&
199
220
  platform_specific_data == o.platform_specific_data
200
221
  end
@@ -208,7 +229,7 @@ module Zernio
208
229
  # Calculates hash code according to all attributes.
209
230
  # @return [Integer] Hash code
210
231
  def hash
211
- [account_id, platform, budget, bid_strategy, name, platform_specific_data].hash
232
+ [platform, account_id, bid_strategy, bid_amount, roas_average_floor, budget, name, platform_specific_data].hash
212
233
  end
213
234
 
214
235
  # Builds the object from hash
@@ -14,6 +14,7 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Zernio
17
+ # **Meta only.** The CBO budget.
17
18
  class UpdateAdCampaignRequestBudget < ApiModelBase
18
19
  # Budget amount in the ad account's currency
19
20
  attr_accessor :amount
@@ -14,9 +14,9 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Zernio
17
- # Platform-specific campaign settings. The platform is implied by the `platform` body param (same convention as platformSpecificData on POST /v1/ads/create). Meta (facebook/instagram) only; other platforms return 400.
17
+ # **Meta only.** Platform implied by the `platform` body param, same convention as POST /v1/ads/create.
18
18
  class UpdateAdCampaignRequestPlatformSpecificData < ApiModelBase
19
- # Campaign lifetime spend cap, in the ad account's currency (Meta `spend_cap`). Pass null to remove the cap (0 is rejected by Meta).
19
+ # Campaign lifetime spend cap, in the ad account's currency (Meta `spend_cap`). Pass null to remove the cap; 0 is rejected by Meta.
20
20
  attr_accessor :spend_cap
21
21
 
22
22
  # Attribute mapping from ruby-style variable name to JSON key.
@@ -11,5 +11,5 @@ Generator version: 7.19.0
11
11
  =end
12
12
 
13
13
  module Zernio
14
- VERSION = '0.0.690'
14
+ VERSION = '0.0.692'
15
15
  end
data/lib/zernio-sdk.rb CHANGED
@@ -993,6 +993,8 @@ require 'zernio-sdk/models/money'
993
993
  require 'zernio-sdk/models/money_amount'
994
994
  require 'zernio-sdk/models/move_account_to_profile200_response'
995
995
  require 'zernio-sdk/models/move_account_to_profile_request'
996
+ require 'zernio-sdk/models/move_whats_app_number_to_profile200_response'
997
+ require 'zernio-sdk/models/move_whats_app_number_to_profile_request'
996
998
  require 'zernio-sdk/models/on_verification_approved_request'
997
999
  require 'zernio-sdk/models/on_verification_approved_request_verification'
998
1000
  require 'zernio-sdk/models/on_verification_failed_request'
data/openapi.yaml CHANGED
@@ -32260,6 +32260,64 @@ paths:
32260
32260
  '400': { description: Country does not require KYC (not a regulated country). }
32261
32261
  '401': { $ref: '#/components/responses/Unauthorized' }
32262
32262
 
32263
+ /v1/whatsapp/phone-numbers/{id}/profile:
32264
+ patch:
32265
+ x-resource-group: "telephony"
32266
+ operationId: moveWhatsAppNumberToProfile
32267
+ tags: [WhatsApp Phone Numbers]
32268
+ summary: Move a number to another profile
32269
+ description: |
32270
+ Move a provisioned number to a different profile.
32271
+
32272
+ A number is not a single record. Alongside the number itself there are
32273
+ hidden telephony owner accounts (platform `phone`, plus `sms` when SMS is
32274
+ enabled) and, once WhatsApp is connected, the `whatsapp` account. They all
32275
+ carry a profileId and this endpoint moves them together.
32276
+
32277
+ Use this instead of `PATCH /v1/accounts/{accountId}`: that one moves the
32278
+ social account only and leaves the number itself pinned to its original
32279
+ profile, which splits the number across two profiles. Connecting a
32280
+ provisioned number always places it on the profile the NUMBER is on, so a
32281
+ `profileId` passed to `GET /v1/connect/whatsapp` cannot re-home it and a
32282
+ later reconnect pulls the account back. This endpoint is how you re-home it.
32283
+
32284
+ `id` is the number record id from `GET /v1/phone-numbers`, not an account id.
32285
+
32286
+ A profile holds at most one account per platform, so the destination must be
32287
+ free of every platform this number occupies.
32288
+ security:
32289
+ - bearerAuth: []
32290
+ parameters:
32291
+ - { name: id, in: path, required: true, schema: { type: string }, description: WhatsAppPhoneNumber id. }
32292
+ requestBody:
32293
+ required: true
32294
+ content:
32295
+ application/json:
32296
+ schema:
32297
+ type: object
32298
+ required: [profileId]
32299
+ properties:
32300
+ profileId: { type: string, description: Destination profile id. Must belong to the same team. }
32301
+ responses:
32302
+ '200':
32303
+ description: Number moved, or already on that profile.
32304
+ content:
32305
+ application/json:
32306
+ schema:
32307
+ type: object
32308
+ properties:
32309
+ message: { type: string }
32310
+ profileId: { type: string, description: The profile the number is now on. }
32311
+ movedPlatforms:
32312
+ type: array
32313
+ items: { type: string }
32314
+ description: 'Platforms whose accounts travelled with the number (phone, sms, whatsapp). Absent when the number was already on the destination profile.'
32315
+ '400': { $ref: '#/components/responses/BadRequest' }
32316
+ '401': { $ref: '#/components/responses/Unauthorized' }
32317
+ '403': { description: 'No access to the source or destination profile, or the Inbox add-on is not active.' }
32318
+ '404': { description: 'Number not found, or the destination profile does not exist.' }
32319
+ '409': { description: 'The destination profile already holds an account on one of the platforms this number occupies.' }
32320
+
32263
32321
  /v1/whatsapp/phone-numbers/{id}/remediate:
32264
32322
  get:
32265
32323
  x-resource-group: "telephony"
@@ -36573,7 +36631,7 @@ paths:
36573
36631
  x-resource-group: "ads"
36574
36632
  operationId: updateAdCampaignStatus
36575
36633
  tags: ["Ad Campaigns"]
36576
- x-platforms: ["meta", "google", "tiktok", "linkedin", "pinterest", "x"]
36634
+ x-platforms: ["meta", "google", "tiktok", "linkedin", "pinterest", "x", "openai"]
36577
36635
  summary: Pause or resume a campaign
36578
36636
  description: |
36579
36637
  Updates the status of all ads in a campaign. Makes one platform API call (not per-ad) since status cascades through the campaign hierarchy.
@@ -36615,26 +36673,31 @@ paths:
36615
36673
  x-resource-group: "ads"
36616
36674
  operationId: updateAdCampaign
36617
36675
  tags: ["Ad Campaigns"]
36618
- x-platforms: ["meta", "google", "tiktok", "linkedin", "pinterest", "x"]
36676
+ x-platforms: ["meta", "google"]
36619
36677
  summary: Update a campaign
36620
36678
  description: |
36621
- Campaign-level edits. At least one of `budget`, `bidStrategy`, `name` or `platformSpecificData` is required.
36679
+ Campaign-level edits. Send at least one of `budget`, `bidStrategy`, `name`
36680
+ or `platformSpecificData`. An unsupported field is always an error, never a
36681
+ silent drop.
36622
36682
 
36623
- **Empty campaigns.** A campaign with zero ads has no local Ad documents to
36624
- resolve, so this would 404 even though it exists on Meta. Send `accountId`
36625
- in the body to skip the local lookup and forward the update to Meta. The
36626
- response then carries `updated: 0`, since there are no local rows to mirror
36627
- onto. `accountId` is ignored when the campaign does have ads.
36683
+ | Body field | Meta | Google | Others |
36684
+ |---|---|---|---|
36685
+ | `bidStrategy` | Yes | Yes | 501 |
36686
+ | `bidAmount`, `roasAverageFloor` | 400 — ad-set level | Yes | 400 |
36687
+ | `budget` (CBO; ABO returns 409) | Yes | 501 | 501 |
36688
+ | `name` | Yes | 501 | 501 |
36689
+ | `platformSpecificData.spendCap` | Yes | 400 | 400 |
36690
+ | `accountId` (empty campaigns) | Yes | - | - |
36628
36691
 
36629
- - `budget` updates the CBO (Campaign Budget Optimization) budget. For ABO campaigns
36630
- (where the budget lives on the ad set), use PUT /v1/ads/ad-sets/{adSetId} instead — this endpoint
36631
- will return 409 with code BUDGET_LEVEL_MISMATCH.
36632
- - `bidStrategy` sets the campaign-level default bid strategy. Per Meta's spec, `bid_amount` and
36633
- `bid_constraints` do NOT exist at the campaign level — pass them via PUT /v1/ads/ad-sets/{adSetId}.
36634
- - `platformSpecificData.spendCap` (Meta only) sets the campaign's lifetime spend cap, in the ad
36635
- account's currency.
36692
+ Google maps the shared enum onto its own strategies: `LOWEST_COST_WITHOUT_CAP`
36693
+ to Maximize Clicks, `LOWEST_COST_WITH_BID_CAP` to Maximize Clicks with a max
36694
+ CPC (`bidAmount`), `COST_CAP` to Target CPA (`bidAmount`),
36695
+ `LOWEST_COST_WITH_MIN_ROAS` to Target ROAS (`roasAverageFloor`). A campaign on
36696
+ a PORTFOLIO bidding strategy is rejected: detach it in Google Ads first, since
36697
+ it is shared across campaigns.
36636
36698
 
36637
- Meta-only for now. Other platforms return 501 Not Implemented.
36699
+ `accountId` forwards the update straight to Meta for a campaign with zero ads,
36700
+ which would otherwise 404; the response then carries `updated: 0`.
36638
36701
  security:
36639
36702
  - bearerAuth: []
36640
36703
  parameters:
@@ -36647,26 +36710,26 @@ paths:
36647
36710
  type: object
36648
36711
  required: [platform]
36649
36712
  properties:
36650
- accountId: { type: string, description: "Zernio SocialAccount id owning the ad account. Required only to update an EMPTY campaign (zero ads), which has no local Ad documents to resolve a token from." }
36651
- platform: { type: string, enum: [facebook, instagram] }
36713
+ platform: { type: string, enum: [facebook, instagram, google], description: "Required: platform campaign IDs are not globally unique." }
36714
+ accountId: { type: string, description: "**Meta only.** Zernio SocialAccount id owning the ad account. Needed only for an EMPTY campaign (zero ads); ignored otherwise." }
36715
+ bidStrategy:
36716
+ allOf: [{ $ref: '#/components/schemas/BidStrategy' }]
36717
+ description: "**Meta + Google.** On Meta, the campaign default that ad sets inherit unless they override it. On Google, the campaign's own bidding strategy."
36718
+ bidAmount: { type: number, description: "**Google only.** Whole currency units (USD: 12 = $12.00). Max CPC for LOWEST_COST_WITH_BID_CAP, CPA target for COST_CAP; required for both." }
36719
+ roasAverageFloor: { type: number, description: "**Google only.** Decimal ROAS multiplier (2.0 = 2.0x), required for LOWEST_COST_WITH_MIN_ROAS." }
36652
36720
  budget:
36653
36721
  type: object
36722
+ description: "**Meta only.** The CBO budget."
36654
36723
  required: [amount, type]
36655
36724
  properties:
36656
36725
  amount: { type: number, description: Budget amount in the ad account's currency }
36657
36726
  type: { type: string, enum: [daily, lifetime] }
36658
- bidStrategy:
36659
- allOf: [{ $ref: '#/components/schemas/BidStrategy' }]
36660
- description: "Campaign-level default. Ad sets inherit this unless they override."
36661
- name: { type: string, maxLength: 255, description: "Rename the campaign (Meta only; other platforms return 501). At least one of budget/bidStrategy/name/platformSpecificData is required." }
36727
+ name: { type: string, maxLength: 255, description: "**Meta only.** Rename the campaign." }
36662
36728
  platformSpecificData:
36663
36729
  type: object
36664
- description: |
36665
- Platform-specific campaign settings. The platform is implied by the `platform`
36666
- body param (same convention as platformSpecificData on POST /v1/ads/create).
36667
- Meta (facebook/instagram) only; other platforms return 400.
36730
+ description: "**Meta only.** Platform implied by the `platform` body param, same convention as POST /v1/ads/create."
36668
36731
  properties:
36669
- spendCap: { type: [number, "null"], description: "Campaign lifetime spend cap, in the ad account's currency (Meta `spend_cap`). Pass null to remove the cap (0 is rejected by Meta)." }
36732
+ spendCap: { type: [number, "null"], description: "Campaign lifetime spend cap, in the ad account's currency (Meta `spend_cap`). Pass null to remove the cap; 0 is rejected by Meta." }
36670
36733
  responses:
36671
36734
  '200':
36672
36735
  description: Campaign updated
@@ -36674,13 +36737,16 @@ paths:
36674
36737
  application/json:
36675
36738
  schema:
36676
36739
  type: object
36740
+ description: Echoes back only the fields you sent, plus `updated`.
36677
36741
  properties:
36678
- updated: { type: integer }
36742
+ updated: { type: integer, description: "Local Ad documents mirrored. 0 on the empty-campaign path." }
36679
36743
  budget: { $ref: '#/components/schemas/AdBudget' }
36680
36744
  budgetLevel: { type: string, enum: [campaign] }
36681
36745
  bidStrategy: { $ref: '#/components/schemas/BidStrategy' }
36746
+ bidAmount: { type: number }
36747
+ roasAverageFloor: { type: number }
36682
36748
  platformSpecificData: { type: object }
36683
- '400': { description: Invalid input }
36749
+ '400': { description: "Invalid input, or a field the resolved platform does not support at the campaign level (see the support table)" }
36684
36750
  '401': { $ref: '#/components/responses/Unauthorized' }
36685
36751
  '404': { description: Campaign not found }
36686
36752
  '409': { description: "Campaign is ABO — route to /v1/ads/ad-sets/{adSetId} instead" }
@@ -36690,16 +36756,13 @@ paths:
36690
36756
  x-resource-group: "ads"
36691
36757
  operationId: deleteAdCampaign
36692
36758
  tags: ["Ad Campaigns"]
36693
- x-platforms: ["meta", "google", "tiktok", "linkedin", "pinterest", "x"]
36759
+ x-platforms: ["meta", "tiktok"]
36694
36760
  summary: Delete a campaign
36695
36761
  description: |
36696
36762
  Deletes the whole campaign on the platform, cascading to its ad sets
36697
36763
  and ads. Locally, all Ad documents for this campaign are marked
36698
36764
  `status: cancelled`.
36699
36765
 
36700
- Meta-only for now. Other platforms return 501 Not Implemented — fall
36701
- back to DELETE /v1/ads/{adId} per ad in the meantime.
36702
-
36703
36766
  **Empty campaigns.** A campaign with zero ads has no local Ad documents
36704
36767
  to resolve, so it is invisible to `/v1/ads/tree` and this endpoint would
36705
36768
  404. That state is produced by the two-step create flow (campaign, then
@@ -96,7 +96,7 @@ describe 'AdCampaignsApi' do
96
96
 
97
97
  # unit tests for delete_ad_campaign
98
98
  # Delete a campaign
99
- # Deletes the whole campaign on the platform, cascading to its ad sets and ads. Locally, all Ad documents for this campaign are marked &#x60;status: cancelled&#x60;. Meta-only for now. Other platforms return 501 Not Implemented — fall back to DELETE /v1/ads/{adId} per ad in the meantime. **Empty campaigns.** A campaign with zero ads has no local Ad documents to resolve, so it is invisible to &#x60;/v1/ads/tree&#x60; and this endpoint would 404. That state is produced by the two-step create flow (campaign, then ads via &#x60;existingCampaignId&#x60;) whenever Meta rejects the ad step. To delete such a shell, send &#x60;accountId&#x60; in the body: we skip the local lookup entirely and forward the delete to Meta. &#x60;accountId&#x60; is ignored when the campaign does have ads.
99
+ # Deletes the whole campaign on the platform, cascading to its ad sets and ads. Locally, all Ad documents for this campaign are marked &#x60;status: cancelled&#x60;. **Empty campaigns.** A campaign with zero ads has no local Ad documents to resolve, so it is invisible to &#x60;/v1/ads/tree&#x60; and this endpoint would 404. That state is produced by the two-step create flow (campaign, then ads via &#x60;existingCampaignId&#x60;) whenever Meta rejects the ad step. To delete such a shell, send &#x60;accountId&#x60; in the body: we skip the local lookup entirely and forward the delete to Meta. &#x60;accountId&#x60; is ignored when the campaign does have ads.
100
100
  # @param campaign_id Platform campaign ID
101
101
  # @param delete_ad_campaign_request
102
102
  # @param [Hash] opts the optional parameters
@@ -303,7 +303,7 @@ describe 'AdCampaignsApi' do
303
303
 
304
304
  # unit tests for update_ad_campaign
305
305
  # Update a campaign
306
- # Campaign-level edits. At least one of &#x60;budget&#x60;, &#x60;bidStrategy&#x60;, &#x60;name&#x60; or &#x60;platformSpecificData&#x60; is required. **Empty campaigns.** A campaign with zero ads has no local Ad documents to resolve, so this would 404 even though it exists on Meta. Send &#x60;accountId&#x60; in the body to skip the local lookup and forward the update to Meta. The response then carries &#x60;updated: 0&#x60;, since there are no local rows to mirror onto. &#x60;accountId&#x60; is ignored when the campaign does have ads. - &#x60;budget&#x60; updates the CBO (Campaign Budget Optimization) budget. For ABO campaigns (where the budget lives on the ad set), use PUT /v1/ads/ad-sets/{adSetId} instead — this endpoint will return 409 with code BUDGET_LEVEL_MISMATCH. - &#x60;bidStrategy&#x60; sets the campaign-level default bid strategy. Per Meta&#39;s spec, &#x60;bid_amount&#x60; and &#x60;bid_constraints&#x60; do NOT exist at the campaign level — pass them via PUT /v1/ads/ad-sets/{adSetId}. - &#x60;platformSpecificData.spendCap&#x60; (Meta only) sets the campaign&#39;s lifetime spend cap, in the ad account&#39;s currency. Meta-only for now. Other platforms return 501 Not Implemented.
306
+ # Campaign-level edits. Send at least one of &#x60;budget&#x60;, &#x60;bidStrategy&#x60;, &#x60;name&#x60; or &#x60;platformSpecificData&#x60;. An unsupported field is always an error, never a silent drop. | Body field | Meta | Google | Others | |---|---|---|---| | &#x60;bidStrategy&#x60; | Yes | Yes | 501 | | &#x60;bidAmount&#x60;, &#x60;roasAverageFloor&#x60; | 400 — ad-set level | Yes | 400 | | &#x60;budget&#x60; (CBO; ABO returns 409) | Yes | 501 | 501 | | &#x60;name&#x60; | Yes | 501 | 501 | | &#x60;platformSpecificData.spendCap&#x60; | Yes | 400 | 400 | | &#x60;accountId&#x60; (empty campaigns) | Yes | - | - | Google maps the shared enum onto its own strategies: &#x60;LOWEST_COST_WITHOUT_CAP&#x60; to Maximize Clicks, &#x60;LOWEST_COST_WITH_BID_CAP&#x60; to Maximize Clicks with a max CPC (&#x60;bidAmount&#x60;), &#x60;COST_CAP&#x60; to Target CPA (&#x60;bidAmount&#x60;), &#x60;LOWEST_COST_WITH_MIN_ROAS&#x60; to Target ROAS (&#x60;roasAverageFloor&#x60;). A campaign on a PORTFOLIO bidding strategy is rejected: detach it in Google Ads first, since it is shared across campaigns. &#x60;accountId&#x60; forwards the update straight to Meta for a campaign with zero ads, which would otherwise 404; the response then carries &#x60;updated: 0&#x60;.
307
307
  # @param campaign_id Platform campaign ID
308
308
  # @param update_ad_campaign_request
309
309
  # @param [Hash] opts the optional parameters