late-sdk 0.0.723 → 0.0.725

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -0
  3. data/docs/AdAudiencesApi.md +74 -2
  4. data/docs/AdCreativesApi.md +80 -0
  5. data/docs/ListAdVideos200Response.md +22 -0
  6. data/docs/ReplaceAdAudienceCompanies200Response.md +20 -0
  7. data/docs/ReplaceAdAudienceCompaniesRequest.md +18 -0
  8. data/docs/ReplaceAdAudienceCompaniesRequestCompaniesInner.md +24 -0
  9. data/docs/UploadedOrDerivedAudience.md +4 -2
  10. data/lib/zernio-sdk/api/ad_audiences_api.rb +78 -4
  11. data/lib/zernio-sdk/api/ad_creatives_api.rb +88 -0
  12. data/lib/zernio-sdk/models/create_standalone_ad_request_video.rb +1 -1
  13. data/lib/zernio-sdk/models/list_ad_videos200_response.rb +167 -0
  14. data/lib/zernio-sdk/models/replace_ad_audience_companies200_response.rb +157 -0
  15. data/lib/zernio-sdk/models/replace_ad_audience_companies_request.rb +185 -0
  16. data/lib/zernio-sdk/models/replace_ad_audience_companies_request_companies_inner.rb +231 -0
  17. data/lib/zernio-sdk/models/uploaded_or_derived_audience.rb +13 -3
  18. data/lib/zernio-sdk/version.rb +1 -1
  19. data/lib/zernio-sdk.rb +4 -0
  20. data/openapi.yaml +167 -9
  21. data/spec/api/ad_audiences_api_spec.rb +15 -2
  22. data/spec/api/ad_creatives_api_spec.rb +16 -0
  23. data/spec/models/list_ad_videos200_response_spec.rb +48 -0
  24. data/spec/models/replace_ad_audience_companies200_response_spec.rb +42 -0
  25. data/spec/models/replace_ad_audience_companies_request_companies_inner_spec.rb +54 -0
  26. data/spec/models/replace_ad_audience_companies_request_spec.rb +36 -0
  27. data/spec/models/uploaded_or_derived_audience_spec.rb +6 -0
  28. data/zernio-sdk-0.0.725.gem +0 -0
  29. metadata +18 -2
  30. data/zernio-sdk-0.0.723.gem +0 -0
@@ -0,0 +1,231 @@
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 ReplaceAdAudienceCompaniesRequestCompaniesInner < ApiModelBase
18
+ attr_accessor :name
19
+
20
+ attr_accessor :domain
21
+
22
+ attr_accessor :website
23
+
24
+ attr_accessor :linkedin_page_url
25
+
26
+ # Attribute mapping from ruby-style variable name to JSON key.
27
+ def self.attribute_map
28
+ {
29
+ :'name' => :'name',
30
+ :'domain' => :'domain',
31
+ :'website' => :'website',
32
+ :'linkedin_page_url' => :'linkedinPageUrl'
33
+ }
34
+ end
35
+
36
+ # Returns attribute mapping this model knows about
37
+ def self.acceptable_attribute_map
38
+ attribute_map
39
+ end
40
+
41
+ # Returns all the JSON keys this model knows about
42
+ def self.acceptable_attributes
43
+ acceptable_attribute_map.values
44
+ end
45
+
46
+ # Attribute type mapping.
47
+ def self.openapi_types
48
+ {
49
+ :'name' => :'String',
50
+ :'domain' => :'String',
51
+ :'website' => :'String',
52
+ :'linkedin_page_url' => :'String'
53
+ }
54
+ end
55
+
56
+ # List of attributes with nullable: true
57
+ def self.openapi_nullable
58
+ Set.new([
59
+ ])
60
+ end
61
+
62
+ # Initializes the object
63
+ # @param [Hash] attributes Model attributes in the form of hash
64
+ def initialize(attributes = {})
65
+ if (!attributes.is_a?(Hash))
66
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Zernio::ReplaceAdAudienceCompaniesRequestCompaniesInner` initialize method"
67
+ end
68
+
69
+ # check to see if the attribute exists and convert string to symbol for hash key
70
+ acceptable_attribute_map = self.class.acceptable_attribute_map
71
+ attributes = attributes.each_with_object({}) { |(k, v), h|
72
+ if (!acceptable_attribute_map.key?(k.to_sym))
73
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Zernio::ReplaceAdAudienceCompaniesRequestCompaniesInner`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
74
+ end
75
+ h[k.to_sym] = v
76
+ }
77
+
78
+ if attributes.key?(:'name')
79
+ self.name = attributes[:'name']
80
+ end
81
+
82
+ if attributes.key?(:'domain')
83
+ self.domain = attributes[:'domain']
84
+ end
85
+
86
+ if attributes.key?(:'website')
87
+ self.website = attributes[:'website']
88
+ end
89
+
90
+ if attributes.key?(:'linkedin_page_url')
91
+ self.linkedin_page_url = attributes[:'linkedin_page_url']
92
+ end
93
+ end
94
+
95
+ # Show invalid properties with the reasons. Usually used together with valid?
96
+ # @return Array for valid properties with the reasons
97
+ def list_invalid_properties
98
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
99
+ invalid_properties = Array.new
100
+ if !@name.nil? && @name.to_s.length > 255
101
+ invalid_properties.push('invalid value for "name", the character length must be smaller than or equal to 255.')
102
+ end
103
+
104
+ if !@domain.nil? && @domain.to_s.length > 100
105
+ invalid_properties.push('invalid value for "domain", the character length must be smaller than or equal to 100.')
106
+ end
107
+
108
+ if !@website.nil? && @website.to_s.length > 100
109
+ invalid_properties.push('invalid value for "website", the character length must be smaller than or equal to 100.')
110
+ end
111
+
112
+ invalid_properties
113
+ end
114
+
115
+ # Check to see if the all the properties in the model are valid
116
+ # @return true if the model is valid
117
+ def valid?
118
+ warn '[DEPRECATED] the `valid?` method is obsolete'
119
+ return false if !@name.nil? && @name.to_s.length > 255
120
+ return false if !@domain.nil? && @domain.to_s.length > 100
121
+ return false if !@website.nil? && @website.to_s.length > 100
122
+ true
123
+ end
124
+
125
+ # Custom attribute writer method with validation
126
+ # @param [Object] name Value to be assigned
127
+ def name=(name)
128
+ if name.nil?
129
+ fail ArgumentError, 'name cannot be nil'
130
+ end
131
+
132
+ if name.to_s.length > 255
133
+ fail ArgumentError, 'invalid value for "name", the character length must be smaller than or equal to 255.'
134
+ end
135
+
136
+ @name = name
137
+ end
138
+
139
+ # Custom attribute writer method with validation
140
+ # @param [Object] domain Value to be assigned
141
+ def domain=(domain)
142
+ if domain.nil?
143
+ fail ArgumentError, 'domain cannot be nil'
144
+ end
145
+
146
+ if domain.to_s.length > 100
147
+ fail ArgumentError, 'invalid value for "domain", the character length must be smaller than or equal to 100.'
148
+ end
149
+
150
+ @domain = domain
151
+ end
152
+
153
+ # Custom attribute writer method with validation
154
+ # @param [Object] website Value to be assigned
155
+ def website=(website)
156
+ if website.nil?
157
+ fail ArgumentError, 'website cannot be nil'
158
+ end
159
+
160
+ if website.to_s.length > 100
161
+ fail ArgumentError, 'invalid value for "website", the character length must be smaller than or equal to 100.'
162
+ end
163
+
164
+ @website = website
165
+ end
166
+
167
+ # Checks equality by comparing each attribute.
168
+ # @param [Object] Object to be compared
169
+ def ==(o)
170
+ return true if self.equal?(o)
171
+ self.class == o.class &&
172
+ name == o.name &&
173
+ domain == o.domain &&
174
+ website == o.website &&
175
+ linkedin_page_url == o.linkedin_page_url
176
+ end
177
+
178
+ # @see the `==` method
179
+ # @param [Object] Object to be compared
180
+ def eql?(o)
181
+ self == o
182
+ end
183
+
184
+ # Calculates hash code according to all attributes.
185
+ # @return [Integer] Hash code
186
+ def hash
187
+ [name, domain, website, linkedin_page_url].hash
188
+ end
189
+
190
+ # Builds the object from hash
191
+ # @param [Hash] attributes Model attributes in the form of hash
192
+ # @return [Object] Returns the model itself
193
+ def self.build_from_hash(attributes)
194
+ return nil unless attributes.is_a?(Hash)
195
+ attributes = attributes.transform_keys(&:to_sym)
196
+ transformed_hash = {}
197
+ openapi_types.each_pair do |key, type|
198
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
199
+ transformed_hash["#{key}"] = nil
200
+ elsif type =~ /\AArray<(.*)>/i
201
+ # check to ensure the input is an array given that the attribute
202
+ # is documented as an array but the input is not
203
+ if attributes[attribute_map[key]].is_a?(Array)
204
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
205
+ end
206
+ elsif !attributes[attribute_map[key]].nil?
207
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
208
+ end
209
+ end
210
+ new(transformed_hash)
211
+ end
212
+
213
+ # Returns the object in the form of hash
214
+ # @return [Hash] Returns the object in the form of hash
215
+ def to_hash
216
+ hash = {}
217
+ self.class.attribute_map.each_pair do |attr, param|
218
+ value = self.send(attr)
219
+ if value.nil?
220
+ is_nullable = self.class.openapi_nullable.include?(attr)
221
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
222
+ end
223
+
224
+ hash[param] = _to_hash(value)
225
+ end
226
+ hash
227
+ end
228
+
229
+ end
230
+
231
+ end
@@ -42,7 +42,7 @@ module Zernio
42
42
  # Required for engagement audiences. Campaign URNs for the ad source types, organization URNs for pages and events. LinkedIn creates one rule per source, all sharing the same trigger and lookbackDays.
43
43
  attr_accessor :engagement_sources
44
44
 
45
- # Required for company_list audiences (LinkedIn only): plain-text company rows for account targeting. Each row needs at least one identifier. LinkedIn recommends 1,000+ companies for a usable match rate and takes up to 48h to process the list.
45
+ # Required for company_list audiences (LinkedIn only): plain-text company rows for account targeting. Each row needs at least one identifier. Not hashed, LinkedIn matches these against its own company graph. LinkedIn recommends 1,000+ companies for a usable match rate and takes up to 48h to process the list. Replace the list later with POST /v1/ads/audiences/{audienceId}/companies.
46
46
  attr_accessor :companies
47
47
 
48
48
  # Required for website audiences
@@ -69,7 +69,10 @@ module Zernio
69
69
  # Required for lookalike audiences
70
70
  attr_accessor :ratio
71
71
 
72
- # Optional raw Meta rule, forwarded verbatim: pixel event rule for website audiences, or the engagement rule for meta_engagement (overrides the built rule, e.g. for event/canvas/lead-form sources).
72
+ # website only. Narrows the audience from all visitors to visitors of URLs containing this substring. Ignored when `rule` is supplied.
73
+ attr_accessor :url_contains
74
+
75
+ # Optional raw Meta rule, replacing the one we build. Omit it for all visitors of `pixelId`, or use `urlContains` for the common page-match case. For `website` this is Meta's Flexible Audience Rule and is VALIDATED before we call Meta: every entry in `inclusions.rules` (and `exclusions.rules`) must carry `event_sources`, `retention_seconds` AND `filter`. Meta rejects a rule missing any of the three with code 100 / subcode 1713098 (\"Invalid rule JSON format\"), so a bad shape is a 400 here instead. The pre-2018 flat shapes (`{url: ...}`, `{event: ...}`) are not accepted by Meta at all (subcode 1870029). Example, visitors of /checkout in the last 30 days: `{\"inclusions\":{\"operator\":\"or\",\"rules\":[{\"event_sources\":[{\"id\":\"<pixelId>\",\"type\":\"pixel\"}],\"retention_seconds\":2592000,\"filter\":{\"operator\":\"and\",\"filters\":[{\"field\":\"url\",\"operator\":\"i_contains\",\"value\":\"/checkout\"}]}}]}}` Note Meta DERIVES `retention_days` from `retention_seconds` and stores `event_sources[].id` as a number, so a rule read back will not be byte-identical to the one you sent. For `meta_engagement` the rule is forwarded verbatim and NOT validated: that type has two dialects (the `video` source uses a legacy flat array), so no single schema covers both.
73
76
  attr_accessor :rule
74
77
 
75
78
  # Data source declaration for GDPR compliance (customer_list only)
@@ -119,6 +122,7 @@ module Zernio
119
122
  :'source_audience_id' => :'sourceAudienceId',
120
123
  :'country' => :'country',
121
124
  :'ratio' => :'ratio',
125
+ :'url_contains' => :'urlContains',
122
126
  :'rule' => :'rule',
123
127
  :'customer_file_source' => :'customerFileSource'
124
128
  }
@@ -156,6 +160,7 @@ module Zernio
156
160
  :'source_audience_id' => :'String',
157
161
  :'country' => :'String',
158
162
  :'ratio' => :'Float',
163
+ :'url_contains' => :'String',
159
164
  :'rule' => :'Object',
160
165
  :'customer_file_source' => :'String'
161
166
  }
@@ -273,6 +278,10 @@ module Zernio
273
278
  self.ratio = attributes[:'ratio']
274
279
  end
275
280
 
281
+ if attributes.key?(:'url_contains')
282
+ self.url_contains = attributes[:'url_contains']
283
+ end
284
+
276
285
  if attributes.key?(:'rule')
277
286
  self.rule = attributes[:'rule']
278
287
  end
@@ -568,6 +577,7 @@ module Zernio
568
577
  source_audience_id == o.source_audience_id &&
569
578
  country == o.country &&
570
579
  ratio == o.ratio &&
580
+ url_contains == o.url_contains &&
571
581
  rule == o.rule &&
572
582
  customer_file_source == o.customer_file_source
573
583
  end
@@ -581,7 +591,7 @@ module Zernio
581
591
  # Calculates hash code according to all attributes.
582
592
  # @return [Integer] Hash code
583
593
  def hash
584
- [account_id, ad_account_id, name, description, type, match_rules, source_type, trigger, lookback_days, engagement_sources, companies, pixel_id, retention_days, engagement_source, source_id, event, source_audience_id, country, ratio, rule, customer_file_source].hash
594
+ [account_id, ad_account_id, name, description, type, match_rules, source_type, trigger, lookback_days, engagement_sources, companies, pixel_id, retention_days, engagement_source, source_id, event, source_audience_id, country, ratio, url_contains, rule, customer_file_source].hash
585
595
  end
586
596
 
587
597
  # Builds the object from hash
@@ -11,5 +11,5 @@ Generator version: 7.19.0
11
11
  =end
12
12
 
13
13
  module Zernio
14
- VERSION = '0.0.723'
14
+ VERSION = '0.0.725'
15
15
  end
data/lib/zernio-sdk.rb CHANGED
@@ -878,6 +878,7 @@ require 'zernio-sdk/models/list_ad_keywords200_response'
878
878
  require 'zernio-sdk/models/list_ad_keywords200_response_keywords_inner'
879
879
  require 'zernio-sdk/models/list_ad_labels200_response'
880
880
  require 'zernio-sdk/models/list_ad_studies200_response'
881
+ require 'zernio-sdk/models/list_ad_videos200_response'
881
882
  require 'zernio-sdk/models/list_ads202_response'
882
883
  require 'zernio-sdk/models/list_ads_business_centers200_response'
883
884
  require 'zernio-sdk/models/list_api_keys200_response'
@@ -1135,6 +1136,9 @@ require 'zernio-sdk/models/remove_bookmark200_response'
1135
1136
  require 'zernio-sdk/models/remove_conversion_associations200_response'
1136
1137
  require 'zernio-sdk/models/remove_discord_member_role200_response'
1137
1138
  require 'zernio-sdk/models/remove_whats_app_group_participants_request'
1139
+ require 'zernio-sdk/models/replace_ad_audience_companies200_response'
1140
+ require 'zernio-sdk/models/replace_ad_audience_companies_request'
1141
+ require 'zernio-sdk/models/replace_ad_audience_companies_request_companies_inner'
1138
1142
  require 'zernio-sdk/models/reply_to_google_business_review200_response'
1139
1143
  require 'zernio-sdk/models/reply_to_google_business_review_request'
1140
1144
  require 'zernio-sdk/models/reply_to_inbox_post200_response'
data/openapi.yaml CHANGED
@@ -42006,7 +42006,7 @@ paths:
42006
42006
  square: { type: string, format: uri, description: "Square 1:1 marketing image URL (e.g. 1080x1080). Required for Google Display." }
42007
42007
  video:
42008
42008
  type: object
42009
- description: "Meta (facebook, instagram) and LinkedIn. When set, creates a VIDEO ad on the legacy (or, for Meta, attach) shape. Mutually exclusive with `imageUrl`. For Meta multi-creative, set `video` per entry inside `creatives[]` instead. For LinkedIn the video is uploaded to LinkedIn under the authoring Company Page (see `organizationId`) and the campaign format is set to SINGLE_VIDEO; LinkedIn ignores `thumbnailUrl` (it auto-generates the poster frame) — supply MP4 H.264/AAC, 3s-30min, 75KB-500MB."
42009
+ description: "Meta (facebook, instagram) and LinkedIn. Creates a single VIDEO ad. Mutually exclusive with `imageUrl`. Supply `url` to upload a file, or `id` to reuse a video already on the ad account (list them with GET /v1/ads/videos). Works on the single-ad and attach (`adSetId`) shapes; for Meta multi-creative, set `video` per entry inside `creatives[]` instead. For LinkedIn the video is uploaded to LinkedIn under the authoring Company Page (see `organizationId`) and the campaign format is set to SINGLE_VIDEO; LinkedIn ignores `thumbnailUrl` (it auto-generates the poster frame) — supply MP4 H.264/AAC, 3s-30min, 75KB-500MB."
42010
42010
  properties:
42011
42011
  url: { type: string, format: uri, description: "Public URL of the video. Meta: uploaded via chunked transfer on /act_X/advideos, then the request blocks on Meta's transcoding until status.video_status === 'ready'. LinkedIn: uploaded via the Videos API (multipart), then the request blocks until LinkedIn finishes transcoding (status AVAILABLE) — short clips take ~10-30s. Provide either `url` or `id`."}
42012
42012
  id: { type: string, description: "Meta only. Reuse a video ALREADY uploaded to this ad account instead of re-uploading the file: pass the `videoId` returned by a previous create. Wins over `url`, so N ads that differ only in copy share one upload (`existingCreativeId` only covers the identical-copy case). Provide either `url` or `id`."}
@@ -43279,6 +43279,53 @@ paths:
43279
43279
  '401': { $ref: '#/components/responses/Unauthorized' }
43280
43280
  '501': { description: Only supported on Meta (facebook/instagram) }
43281
43281
 
43282
+ /v1/ads/videos:
43283
+ get:
43284
+ x-resource-group: "ads"
43285
+ operationId: listAdVideos
43286
+ tags: ["Ad Creatives"]
43287
+ x-platforms: ["meta"]
43288
+ summary: Ad video library
43289
+ description: |-
43290
+ Lists the ad account's video library (Meta's `/act_X/advideos`), rows returned verbatim.
43291
+ The default projection covers id, title, status, poster frames and length; `fields` is a
43292
+ raw-passthrough override. Any `id` here is reusable as `video.id` on the create
43293
+ endpoints, so N ads that differ only in copy share one upload.
43294
+
43295
+ This is the only way to reach a video uploaded OUTSIDE Zernio (Ads Manager, another
43296
+ tool); videos we uploaded also come back as `creative.videoId` on GET /v1/ads.
43297
+
43298
+ Meta transcodes asynchronously, so a row is only usable once `status.video_status`
43299
+ reads `ready`. There is no upload operation here: upload by URL inline via `video.url`
43300
+ on POST /v1/ads/create.
43301
+ security:
43302
+ - bearerAuth: []
43303
+ parameters:
43304
+ - { name: accountId, in: query, required: true, schema: { type: string }, description: "Zernio SocialAccount id (posting or ads variant) used to resolve the Meta token." }
43305
+ - { name: adAccountId, in: query, required: true, schema: { type: string }, description: "Meta ad account id (act_<n>)." }
43306
+ - { name: fields, in: query, schema: { type: string }, description: "Comma-separated Graph field override (supports nested {} projections)." }
43307
+ - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 }, description: Rows per page }
43308
+ - { name: after, in: query, schema: { type: string }, description: "Cursor from paging.after of the previous page." }
43309
+ responses:
43310
+ '200':
43311
+ description: Ad videos (raw Meta shape)
43312
+ content:
43313
+ application/json:
43314
+ schema:
43315
+ type: object
43316
+ properties:
43317
+ adAccountId: { type: string }
43318
+ data:
43319
+ type: array
43320
+ items: { type: object, description: "Raw Meta ad video row (id, title, status, picture, thumbnails, length)." }
43321
+ paging:
43322
+ type: object
43323
+ properties:
43324
+ after: { type: [string, "null"], description: "Cursor for the next page; null when exhausted." }
43325
+ '400': { $ref: '#/components/responses/BadRequest' }
43326
+ '401': { $ref: '#/components/responses/Unauthorized' }
43327
+ '501': { description: Only supported on Meta (facebook/instagram) }
43328
+
43282
43329
  /v1/ads/interests:
43283
43330
  get:
43284
43331
  x-resource-group: "ads"
@@ -43737,11 +43784,21 @@ paths:
43737
43784
  summary: Create custom audience
43738
43785
  description: |
43739
43786
  Create a custom audience. `customer_list` is supported on Meta, Google, X, LinkedIn, TikTok, and Pinterest;
43740
- `website` and `lookalike` are Meta-only. `saved_targeting` stores a reusable TargetingSpec (no member upload,
43741
- no adAccountId) that you reference later via `savedTargetingId` on `POST /v1/ads/create`. Upload-backed audiences
43742
- are created empty, add members via `POST /v1/ads/audiences/{audienceId}/users`. On TikTok and Pinterest the
43743
- audience is provisioned lazily on the first member upload (until then its status is `pending`). Create is not
43744
- idempotent, never auto-retry.
43787
+ `website` and `lookalike` are Meta-only; `company_list`, `engagement` and `website_retargeting` are LinkedIn-only.
43788
+ `saved_targeting` stores a reusable TargetingSpec (no member upload, no adAccountId) that you reference later via
43789
+ `savedTargetingId` on `POST /v1/ads/create`.
43790
+
43791
+ How the audience gets filled depends on the type:
43792
+
43793
+ - `customer_list` is created empty. Add members with `POST /v1/ads/audiences/{audienceId}/users`.
43794
+ On TikTok and Pinterest the audience is provisioned lazily on that first upload (until then its status is `pending`).
43795
+ - `company_list` is filled AT CREATION from the `companies` array below, which is required. To change the list
43796
+ afterwards send the new full list to `POST /v1/ads/audiences/{audienceId}/companies` (a replace, not a merge).
43797
+ The `/users` endpoint rejects these audiences with a 422.
43798
+ - `website`, `website_retargeting`, `engagement`, `meta_engagement` and `lookalike` fill themselves from the pixel,
43799
+ engagement source or seed audience you point them at. They take no member upload at all.
43800
+
43801
+ Create is not idempotent, never auto-retry.
43745
43802
  security:
43746
43803
  - bearerAuth: []
43747
43804
  requestBody:
@@ -43819,8 +43876,10 @@ paths:
43819
43876
  description: |
43820
43877
  Required for company_list audiences (LinkedIn only): plain-text
43821
43878
  company rows for account targeting. Each row needs at least one
43822
- identifier. LinkedIn recommends 1,000+ companies for a usable
43823
- match rate and takes up to 48h to process the list.
43879
+ identifier. Not hashed, LinkedIn matches these against its own
43880
+ company graph. LinkedIn recommends 1,000+ companies for a usable
43881
+ match rate and takes up to 48h to process the list. Replace the
43882
+ list later with POST /v1/ads/audiences/{audienceId}/companies.
43824
43883
  items:
43825
43884
  type: object
43826
43885
  additionalProperties: false
@@ -43850,7 +43909,36 @@ paths:
43850
43909
  sourceAudienceId: { type: string, description: Required for lookalike audiences }
43851
43910
  country: { type: string, description: "2-letter code, required for lookalike audiences" }
43852
43911
  ratio: { type: number, minimum: 0.01, maximum: 0.20, description: Required for lookalike audiences }
43853
- rule: { type: object, description: "Optional raw Meta rule, forwarded verbatim: pixel event rule for website audiences, or the engagement rule for meta_engagement (overrides the built rule, e.g. for event/canvas/lead-form sources)." }
43912
+ urlContains:
43913
+ type: string
43914
+ description: |
43915
+ website only. Narrows the audience from all visitors to visitors of
43916
+ URLs containing this substring. Ignored when `rule` is supplied.
43917
+ rule:
43918
+ type: object
43919
+ description: |
43920
+ Optional raw Meta rule, replacing the one we build. Omit it for all
43921
+ visitors of `pixelId`, or use `urlContains` for the common page-match
43922
+ case.
43923
+
43924
+ For `website` this is Meta's Flexible Audience Rule and is VALIDATED
43925
+ before we call Meta: every entry in `inclusions.rules` (and
43926
+ `exclusions.rules`) must carry `event_sources`, `retention_seconds`
43927
+ AND `filter`. Meta rejects a rule missing any of the three with code
43928
+ 100 / subcode 1713098 ("Invalid rule JSON format"), so a bad shape is
43929
+ a 400 here instead. The pre-2018 flat shapes (`{url: ...}`,
43930
+ `{event: ...}`) are not accepted by Meta at all (subcode 1870029).
43931
+
43932
+ Example, visitors of /checkout in the last 30 days:
43933
+ `{"inclusions":{"operator":"or","rules":[{"event_sources":[{"id":"<pixelId>","type":"pixel"}],"retention_seconds":2592000,"filter":{"operator":"and","filters":[{"field":"url","operator":"i_contains","value":"/checkout"}]}}]}}`
43934
+
43935
+ Note Meta DERIVES `retention_days` from `retention_seconds` and
43936
+ stores `event_sources[].id` as a number, so a rule read back will
43937
+ not be byte-identical to the one you sent.
43938
+
43939
+ For `meta_engagement` the rule is forwarded verbatim and NOT
43940
+ validated: that type has two dialects (the `video` source uses a
43941
+ legacy flat array), so no single schema covers both.
43854
43942
  customerFileSource: { type: string, description: "Data source declaration for GDPR compliance (customer_list only)" }
43855
43943
  - type: object
43856
43944
  title: SavedTargetingAudience
@@ -43993,6 +44081,9 @@ paths:
43993
44081
  Upload user data to a customer_list audience. Data is SHA256-hashed server-side before sending to the platform.
43994
44082
  Email is used on every platform; phone is used on Meta only (other platforms ignore it). On TikTok and Pinterest,
43995
44083
  the first upload also provisions the audience (deferred create). LinkedIn uploads are full-replace. Max 10,000 users per request.
44084
+
44085
+ customer_list only. A LinkedIn `company_list` audience takes company rows, not people: send those to
44086
+ `POST /v1/ads/audiences/{audienceId}/companies`. This endpoint 422s for every other audience type.
43996
44087
  security:
43997
44088
  - bearerAuth: []
43998
44089
  parameters:
@@ -44034,6 +44125,73 @@ paths:
44034
44125
  '422':
44035
44126
  description: Audience is not a customer_list type or has no platform ID yet
44036
44127
 
44128
+ /v1/ads/audiences/{audienceId}/companies:
44129
+ post:
44130
+ x-resource-group: "ads"
44131
+ operationId: replaceAdAudienceCompanies
44132
+ tags: ["Ad Audiences"]
44133
+ x-platforms: ["linkedin"]
44134
+ summary: Replace audience companies
44135
+ description: |
44136
+ Upload the company rows of a LinkedIn `company_list` audience (account-based marketing).
44137
+ LinkedIn-only, every other platform returns 422.
44138
+
44139
+ A LinkedIn audience segment holds exactly one uploaded list, so the list you send here REPLACES the
44140
+ segment's list instead of being appended to it: always send the full set of companies. LinkedIn returns
44141
+ only the identifier of the uploaded file, never its rows, so the merge cannot be done for you, keep the
44142
+ source list on your side. LinkedIn does not document how quickly companies dropped from the list stop
44143
+ being targeted, so treat removals as eventual rather than immediate.
44144
+ Rows are plain text (not hashed), matched against LinkedIn's own company graph. Matching is asynchronous:
44145
+ LinkedIn takes up to 48h for a new audience and up to 24h for a later update, and the audience stays
44146
+ `processing` meanwhile. LinkedIn recommends at least 1,000 companies for a usable match rate, and caps a
44147
+ list at 300,000.
44148
+
44149
+ The initial list is sent with `companies` on `POST /v1/ads/audiences`; this endpoint is for every
44150
+ change after that.
44151
+ security:
44152
+ - bearerAuth: []
44153
+ parameters:
44154
+ - { name: audienceId, in: path, required: true, schema: { type: string } }
44155
+ requestBody:
44156
+ required: true
44157
+ content:
44158
+ application/json:
44159
+ schema:
44160
+ type: object
44161
+ required: [companies]
44162
+ properties:
44163
+ companies:
44164
+ type: array
44165
+ minItems: 1
44166
+ maxItems: 300000
44167
+ description: 'The complete company list. Each row needs at least one of name, domain, website or linkedinPageUrl.'
44168
+ items:
44169
+ type: object
44170
+ additionalProperties: false
44171
+ properties:
44172
+ name: { type: string, maxLength: 255 }
44173
+ domain: { type: string, maxLength: 100 }
44174
+ website: { type: string, maxLength: 100 }
44175
+ linkedinPageUrl: { type: string }
44176
+ responses:
44177
+ '200':
44178
+ description: Companies uploaded
44179
+ content:
44180
+ application/json:
44181
+ schema:
44182
+ type: object
44183
+ properties:
44184
+ message: { type: string }
44185
+ numReceived: { type: integer, description: 'Rows sent to LinkedIn. Matching happens asynchronously, so this is not the matched company count.' }
44186
+ '400':
44187
+ description: 'Invalid input (malformed audienceId, empty companies array, a row with no identifier)'
44188
+ '401': { $ref: '#/components/responses/Unauthorized' }
44189
+ '403':
44190
+ description: Ads access required. Legacy plans need the Ads add-on; included by default on usage-based plans.
44191
+ '404': { $ref: '#/components/responses/NotFound' }
44192
+ '422':
44193
+ description: 'Audience is not a company_list type, is not on LinkedIn, or has no platform ID yet'
44194
+
44037
44195
  /v1/ads/conversions/quality:
44038
44196
  get:
44039
44197
  x-resource-group: "ads"
@@ -34,7 +34,7 @@ describe 'AdAudiencesApi' do
34
34
 
35
35
  # unit tests for add_users_to_ad_audience
36
36
  # Add users to audience
37
- # Upload user data to a customer_list audience. Data is SHA256-hashed server-side before sending to the platform. Email is used on every platform; phone is used on Meta only (other platforms ignore it). On TikTok and Pinterest, the first upload also provisions the audience (deferred create). LinkedIn uploads are full-replace. Max 10,000 users per request.
37
+ # Upload user data to a customer_list audience. Data is SHA256-hashed server-side before sending to the platform. Email is used on every platform; phone is used on Meta only (other platforms ignore it). On TikTok and Pinterest, the first upload also provisions the audience (deferred create). LinkedIn uploads are full-replace. Max 10,000 users per request. customer_list only. A LinkedIn &#x60;company_list&#x60; audience takes company rows, not people: send those to &#x60;POST /v1/ads/audiences/{audienceId}/companies&#x60;. This endpoint 422s for every other audience type.
38
38
  # @param audience_id
39
39
  # @param add_users_to_ad_audience_request
40
40
  # @param [Hash] opts the optional parameters
@@ -47,7 +47,7 @@ describe 'AdAudiencesApi' do
47
47
 
48
48
  # unit tests for create_ad_audience
49
49
  # Create custom audience
50
- # Create a custom audience. &#x60;customer_list&#x60; is supported on Meta, Google, X, LinkedIn, TikTok, and Pinterest; &#x60;website&#x60; and &#x60;lookalike&#x60; are Meta-only. &#x60;saved_targeting&#x60; stores a reusable TargetingSpec (no member upload, no adAccountId) that you reference later via &#x60;savedTargetingId&#x60; on &#x60;POST /v1/ads/create&#x60;. Upload-backed audiences are created empty, add members via &#x60;POST /v1/ads/audiences/{audienceId}/users&#x60;. On TikTok and Pinterest the audience is provisioned lazily on the first member upload (until then its status is &#x60;pending&#x60;). Create is not idempotent, never auto-retry.
50
+ # Create a custom audience. &#x60;customer_list&#x60; is supported on Meta, Google, X, LinkedIn, TikTok, and Pinterest; &#x60;website&#x60; and &#x60;lookalike&#x60; are Meta-only; &#x60;company_list&#x60;, &#x60;engagement&#x60; and &#x60;website_retargeting&#x60; are LinkedIn-only. &#x60;saved_targeting&#x60; stores a reusable TargetingSpec (no member upload, no adAccountId) that you reference later via &#x60;savedTargetingId&#x60; on &#x60;POST /v1/ads/create&#x60;. How the audience gets filled depends on the type: - &#x60;customer_list&#x60; is created empty. Add members with &#x60;POST /v1/ads/audiences/{audienceId}/users&#x60;. On TikTok and Pinterest the audience is provisioned lazily on that first upload (until then its status is &#x60;pending&#x60;). - &#x60;company_list&#x60; is filled AT CREATION from the &#x60;companies&#x60; array below, which is required. To change the list afterwards send the new full list to &#x60;POST /v1/ads/audiences/{audienceId}/companies&#x60; (a replace, not a merge). The &#x60;/users&#x60; endpoint rejects these audiences with a 422. - &#x60;website&#x60;, &#x60;website_retargeting&#x60;, &#x60;engagement&#x60;, &#x60;meta_engagement&#x60; and &#x60;lookalike&#x60; fill themselves from the pixel, engagement source or seed audience you point them at. They take no member upload at all. Create is not idempotent, never auto-retry.
51
51
  # @param create_ad_audience_request
52
52
  # @param [Hash] opts the optional parameters
53
53
  # @return [CreateAdAudience201Response]
@@ -96,6 +96,19 @@ describe 'AdAudiencesApi' do
96
96
  end
97
97
  end
98
98
 
99
+ # unit tests for replace_ad_audience_companies
100
+ # Replace audience companies
101
+ # Upload the company rows of a LinkedIn &#x60;company_list&#x60; audience (account-based marketing). LinkedIn-only, every other platform returns 422. A LinkedIn audience segment holds exactly one uploaded list, so the list you send here REPLACES the segment&#39;s list instead of being appended to it: always send the full set of companies. LinkedIn returns only the identifier of the uploaded file, never its rows, so the merge cannot be done for you, keep the source list on your side. LinkedIn does not document how quickly companies dropped from the list stop being targeted, so treat removals as eventual rather than immediate. Rows are plain text (not hashed), matched against LinkedIn&#39;s own company graph. Matching is asynchronous: LinkedIn takes up to 48h for a new audience and up to 24h for a later update, and the audience stays &#x60;processing&#x60; meanwhile. LinkedIn recommends at least 1,000 companies for a usable match rate, and caps a list at 300,000. The initial list is sent with &#x60;companies&#x60; on &#x60;POST /v1/ads/audiences&#x60;; this endpoint is for every change after that.
102
+ # @param audience_id
103
+ # @param replace_ad_audience_companies_request
104
+ # @param [Hash] opts the optional parameters
105
+ # @return [ReplaceAdAudienceCompanies200Response]
106
+ describe 'replace_ad_audience_companies test' do
107
+ it 'should work' do
108
+ # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
109
+ end
110
+ end
111
+
99
112
  # unit tests for update_ad_audience
100
113
  # Update an audience
101
114
  # Update an audience. &#x60;saved_targeting&#x60; audiences accept &#x60;name&#x60;, &#x60;description&#x60;, and &#x60;spec&#x60; (full replacement, no merge, Zernio-only, no platform call). Platform audiences (uploaded/website/lookalike) accept &#x60;name&#x60; and &#x60;description&#x60; only, updated on the platform first and then mirrored locally; their rules are immutable, so &#x60;spec&#x60; returns 400 for them. Platform audience updates are Meta-only for now (other platforms return 501). Ads already created from a saved_targeting audience are unaffected, they snapshot the targeting at creation.
@@ -154,6 +154,22 @@ describe 'AdCreativesApi' do
154
154
  end
155
155
  end
156
156
 
157
+ # unit tests for list_ad_videos
158
+ # Ad video library
159
+ # Lists the ad account&#39;s video library (Meta&#39;s &#x60;/act_X/advideos&#x60;), rows returned verbatim. The default projection covers id, title, status, poster frames and length; &#x60;fields&#x60; is a raw-passthrough override. Any &#x60;id&#x60; here is reusable as &#x60;video.id&#x60; on the create endpoints, so N ads that differ only in copy share one upload. This is the only way to reach a video uploaded OUTSIDE Zernio (Ads Manager, another tool); videos we uploaded also come back as &#x60;creative.videoId&#x60; on GET /v1/ads. Meta transcodes asynchronously, so a row is only usable once &#x60;status.video_status&#x60; reads &#x60;ready&#x60;. There is no upload operation here: upload by URL inline via &#x60;video.url&#x60; on POST /v1/ads/create.
160
+ # @param account_id Zernio SocialAccount id (posting or ads variant) used to resolve the Meta token.
161
+ # @param ad_account_id Meta ad account id (act_&lt;n&gt;).
162
+ # @param [Hash] opts the optional parameters
163
+ # @option opts [String] :fields Comma-separated Graph field override (supports nested {} projections).
164
+ # @option opts [Integer] :limit Rows per page
165
+ # @option opts [String] :after Cursor from paging.after of the previous page.
166
+ # @return [ListAdVideos200Response]
167
+ describe 'list_ad_videos test' do
168
+ it 'should work' do
169
+ # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
170
+ end
171
+ end
172
+
157
173
  # unit tests for update_ad_creative
158
174
  # Rename a creative
159
175
  # Renames a creative. Creatives are immutable on Meta beyond &#x60;name&#x60; — for content changes create a new creative (POST /v1/ads/creatives) and swap it onto the ad (PUT /v1/ads/{adId} with &#x60;creative&#x60;).