late-sdk 0.0.896 → 0.0.898
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.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/docs/AdCampaignsApi.md +12 -8
- data/docs/AdCreative.md +6 -0
- data/docs/AdCreativesApi.md +2 -2
- data/docs/BoostPostRequest.md +5 -3
- data/docs/CreateAdCreative201Response.md +5 -1
- data/docs/CreateAdCreativeRequest.md +4 -2
- data/docs/CreateCallAdRequest.md +3 -1
- data/docs/CreateMessagingAdRequest.md +3 -1
- data/docs/CreateStandaloneAdRequest.md +5 -3
- data/docs/CreateStandaloneAdRequestCreativesInner.md +4 -0
- data/docs/CreateStandaloneAdRequestPromotedObject.md +2 -2
- data/docs/CtwaAdRequestBody.md +3 -1
- data/docs/CtwaAdRequestBodyCreativesInner.md +2 -0
- data/docs/MetaPromotion.md +26 -0
- data/docs/MetaPromotionStatus.md +15 -0
- data/docs/UpdateAdRequestCreative.md +4 -0
- data/lib/zernio-sdk/api/ad_campaigns_api.rb +11 -8
- data/lib/zernio-sdk/api/ad_creatives_api.rb +4 -4
- data/lib/zernio-sdk/models/ad_creative.rb +52 -1
- data/lib/zernio-sdk/models/boost_post_request.rb +16 -4
- data/lib/zernio-sdk/models/create_ad_creative201_response.rb +44 -4
- data/lib/zernio-sdk/models/create_ad_creative_request.rb +11 -2
- data/lib/zernio-sdk/models/create_call_ad_request.rb +14 -2
- data/lib/zernio-sdk/models/create_messaging_ad_request.rb +14 -2
- data/lib/zernio-sdk/models/create_standalone_ad_request.rb +12 -3
- data/lib/zernio-sdk/models/create_standalone_ad_request_creatives_inner.rb +23 -1
- data/lib/zernio-sdk/models/create_standalone_ad_request_dynamic_creative.rb +1 -1
- data/lib/zernio-sdk/models/create_standalone_ad_request_promoted_object.rb +2 -2
- data/lib/zernio-sdk/models/ctwa_ad_request_body.rb +14 -2
- data/lib/zernio-sdk/models/ctwa_ad_request_body_creatives_inner.rb +35 -1
- data/lib/zernio-sdk/models/meta_promotion.rb +275 -0
- data/lib/zernio-sdk/models/meta_promotion_status.rb +41 -0
- data/lib/zernio-sdk/models/update_ad_request_creative.rb +45 -2
- data/lib/zernio-sdk/version.rb +1 -1
- data/lib/zernio-sdk.rb +2 -0
- data/openapi.yaml +141 -18
- data/spec/api/ad_campaigns_api_spec.rb +5 -4
- data/spec/api/ad_creatives_api_spec.rb +2 -2
- data/spec/models/ad_creative_spec.rb +18 -0
- data/spec/models/boost_post_request_spec.rb +10 -0
- data/spec/models/create_ad_creative201_response_spec.rb +12 -0
- data/spec/models/create_ad_creative_request_spec.rb +6 -0
- data/spec/models/create_call_ad_request_spec.rb +10 -0
- data/spec/models/create_messaging_ad_request_spec.rb +10 -0
- data/spec/models/create_standalone_ad_request_creatives_inner_spec.rb +16 -0
- data/spec/models/create_standalone_ad_request_spec.rb +6 -0
- data/spec/models/ctwa_ad_request_body_creatives_inner_spec.rb +10 -0
- data/spec/models/ctwa_ad_request_body_spec.rb +10 -0
- data/spec/models/meta_promotion_spec.rb +64 -0
- data/spec/models/meta_promotion_status_spec.rb +30 -0
- data/spec/models/update_ad_request_creative_spec.rb +16 -0
- data/zernio-sdk-0.0.898.gem +0 -0
- metadata +10 -2
- data/zernio-sdk-0.0.896.gem +0 -0
|
@@ -14,8 +14,13 @@ require 'date'
|
|
|
14
14
|
require 'time'
|
|
15
15
|
|
|
16
16
|
module Zernio
|
|
17
|
-
# Replace or patch the ad's creative. Meta, TikTok, and LinkedIn. - **Meta**: patch-style. Pass any subset: fields you omit are preserved from the live creative, including media (`image_hash`/`video_id` are reused, no re-upload) and `url_tags`. Sending the full set (`headline`, `body`, `callToAction`, `linkUrl`, `imageUrl`) rebuilds the creative from scratch instead. Partial patching reads the live `object_story_spec`, which Meta strips on SHARE / page-post / dark / asset_feed creatives. Those return 422 asking for the full set. A `videoUrl`/`videoId` on an image creative is a type change and also needs the full set. `existingCreativeId` repoints the ad at a creative from GET /v1/ads/creatives and ignores every other field. Meta creatives are immutable, so any change creates a new creative and repoints the ad; the old creative is retained on the ad account for historical reporting. - **TikTok**: patch-style. Pass any subset; `headline` is ignored (TikTok creatives have no headline slot). `body` becomes the in-feed `ad_text`; `linkUrl` becomes `landing_page_url`; `videoUrl` triggers a fresh upload. `description`, `videoId` and `existingCreativeId` are Meta-only and return 400. - **LinkedIn**: requires new media (image via `imageUrl` or video via `videoUrl`); a text-only creative update returns 400. Uploads the media, creates a new inline media creative on the same campaign, and pauses the old creative (best-effort). The old creative is retained for historical reporting. `videoId` and `existingCreativeId` are Meta-only and return 400.
|
|
17
|
+
# Replace or patch the ad's creative. Meta, TikTok, and LinkedIn. - **Meta**: patch-style. Pass any subset: fields you omit are preserved from the live creative, including media (`image_hash`/`video_id` are reused, no re-upload) and `url_tags`. Sending the full set (`headline`, `body`, `callToAction`, `linkUrl`, `imageUrl`) rebuilds the creative from scratch instead. Partial patching reads the live `object_story_spec`, which Meta strips on SHARE / page-post / dark / asset_feed creatives. Those return 422 asking for the full set. A `videoUrl`/`videoId` on an image creative is a type change and also needs the full set. `existingCreativeId` repoints the ad at a creative from GET /v1/ads/creatives and ignores every other field. Meta creatives are immutable, so any change creates a new creative and repoints the ad; the old creative is retained on the ad account for historical reporting. `promotion` and `creativeFeatures` are Meta-only. Omitted settings are preserved from the live creative, including full rebuilds. Send `promotion: null` to remove the explicit offer from the replacement. A supplied creativeFeatures map overrides individual existing keys. - **TikTok**: patch-style. Pass any subset; `headline` is ignored (TikTok creatives have no headline slot). `body` becomes the in-feed `ad_text`; `linkUrl` becomes `landing_page_url`; `videoUrl` triggers a fresh upload. `description`, `videoId` and `existingCreativeId` are Meta-only and return 400. - **LinkedIn**: requires new media (image via `imageUrl` or video via `videoUrl`); a text-only creative update returns 400. Uploads the media, creates a new inline media creative on the same campaign, and pauses the old creative (best-effort). The old creative is retained for historical reporting. `videoId` and `existingCreativeId` are Meta-only and return 400.
|
|
18
18
|
class UpdateAdRequestCreative < ApiModelBase
|
|
19
|
+
attr_accessor :promotion
|
|
20
|
+
|
|
21
|
+
# Meta Advantage+ creative enhancements. Map snake_case feature names to OPT_IN or OPT_OUT; Meta validates supported keys and unspecified features default to OPT_OUT. auto_promotion_tag is an enhancement; use the separate promotion field for an explicit offer. The deprecated standard_enhancements bundle is rejected by Meta.
|
|
22
|
+
attr_accessor :creative_features
|
|
23
|
+
|
|
19
24
|
# Meta and LinkedIn (TikTok has no headline slot)
|
|
20
25
|
attr_accessor :headline
|
|
21
26
|
|
|
@@ -38,9 +43,33 @@ module Zernio
|
|
|
38
43
|
# Meta only. Repoint the ad at an existing library creative (from GET /v1/ads/creatives); all other creative fields are ignored.
|
|
39
44
|
attr_accessor :existing_creative_id
|
|
40
45
|
|
|
46
|
+
class EnumAttributeValidator
|
|
47
|
+
attr_reader :datatype
|
|
48
|
+
attr_reader :allowable_values
|
|
49
|
+
|
|
50
|
+
def initialize(datatype, allowable_values)
|
|
51
|
+
@allowable_values = allowable_values.map do |value|
|
|
52
|
+
case datatype.to_s
|
|
53
|
+
when /Integer/i
|
|
54
|
+
value.to_i
|
|
55
|
+
when /Float/i
|
|
56
|
+
value.to_f
|
|
57
|
+
else
|
|
58
|
+
value
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def valid?(value)
|
|
64
|
+
!value || allowable_values.include?(value)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
41
68
|
# Attribute mapping from ruby-style variable name to JSON key.
|
|
42
69
|
def self.attribute_map
|
|
43
70
|
{
|
|
71
|
+
:'promotion' => :'promotion',
|
|
72
|
+
:'creative_features' => :'creativeFeatures',
|
|
44
73
|
:'headline' => :'headline',
|
|
45
74
|
:'body' => :'body',
|
|
46
75
|
:'description' => :'description',
|
|
@@ -66,6 +95,8 @@ module Zernio
|
|
|
66
95
|
# Attribute type mapping.
|
|
67
96
|
def self.openapi_types
|
|
68
97
|
{
|
|
98
|
+
:'promotion' => :'MetaPromotion',
|
|
99
|
+
:'creative_features' => :'Hash<String, String>',
|
|
69
100
|
:'headline' => :'String',
|
|
70
101
|
:'body' => :'String',
|
|
71
102
|
:'description' => :'String',
|
|
@@ -100,6 +131,16 @@ module Zernio
|
|
|
100
131
|
h[k.to_sym] = v
|
|
101
132
|
}
|
|
102
133
|
|
|
134
|
+
if attributes.key?(:'promotion')
|
|
135
|
+
self.promotion = attributes[:'promotion']
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
if attributes.key?(:'creative_features')
|
|
139
|
+
if (value = attributes[:'creative_features']).is_a?(Hash)
|
|
140
|
+
self.creative_features = value
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
103
144
|
if attributes.key?(:'headline')
|
|
104
145
|
self.headline = attributes[:'headline']
|
|
105
146
|
end
|
|
@@ -176,6 +217,8 @@ module Zernio
|
|
|
176
217
|
def ==(o)
|
|
177
218
|
return true if self.equal?(o)
|
|
178
219
|
self.class == o.class &&
|
|
220
|
+
promotion == o.promotion &&
|
|
221
|
+
creative_features == o.creative_features &&
|
|
179
222
|
headline == o.headline &&
|
|
180
223
|
body == o.body &&
|
|
181
224
|
description == o.description &&
|
|
@@ -196,7 +239,7 @@ module Zernio
|
|
|
196
239
|
# Calculates hash code according to all attributes.
|
|
197
240
|
# @return [Integer] Hash code
|
|
198
241
|
def hash
|
|
199
|
-
[headline, body, description, call_to_action, link_url, image_url, video_url, video_id, existing_creative_id].hash
|
|
242
|
+
[promotion, creative_features, headline, body, description, call_to_action, link_url, image_url, video_url, video_id, existing_creative_id].hash
|
|
200
243
|
end
|
|
201
244
|
|
|
202
245
|
# Builds the object from hash
|
data/lib/zernio-sdk/version.rb
CHANGED
data/lib/zernio-sdk.rb
CHANGED
|
@@ -1200,6 +1200,8 @@ require 'zernio-sdk/models/media_upload_response'
|
|
|
1200
1200
|
require 'zernio-sdk/models/meta_ads_platform_data'
|
|
1201
1201
|
require 'zernio-sdk/models/meta_lead_form_platform_data'
|
|
1202
1202
|
require 'zernio-sdk/models/meta_lead_form_platform_data_context_card'
|
|
1203
|
+
require 'zernio-sdk/models/meta_promotion'
|
|
1204
|
+
require 'zernio-sdk/models/meta_promotion_status'
|
|
1203
1205
|
require 'zernio-sdk/models/money'
|
|
1204
1206
|
require 'zernio-sdk/models/money_amount'
|
|
1205
1207
|
require 'zernio-sdk/models/move_account_to_profile200_response'
|
data/openapi.yaml
CHANGED
|
@@ -886,6 +886,9 @@ components:
|
|
|
886
886
|
The route enforces this at the Zod boundary; OpenAPI's
|
|
887
887
|
`required` cannot express the OR cleanly.
|
|
888
888
|
properties:
|
|
889
|
+
creativeFeatures:
|
|
890
|
+
$ref: '#/components/schemas/MetaCreativeFeatures'
|
|
891
|
+
description: 'Meta enhancement settings for single or attached ads, and defaults for creatives[]. An item replaces the entire map, including with an empty object.'
|
|
889
892
|
accountId:
|
|
890
893
|
type: string
|
|
891
894
|
minLength: 1
|
|
@@ -913,7 +916,7 @@ components:
|
|
|
913
916
|
whatsappPhoneNumber:
|
|
914
917
|
type: string
|
|
915
918
|
pattern: '^\+[1-9]\d{6,14}$'
|
|
916
|
-
description: 'WhatsApp only. Optional E.164 number already paired with the Facebook Page. Omit to let Meta select the paired number. Sent to the creative CTA and, when creating a new ad set, its promoted_object. Attach requests do not change the existing ad set.'
|
|
919
|
+
description: 'WhatsApp only. Optional E.164 number already paired with the Facebook Page. Omit to let Meta select the paired number. Sent to the creative CTA and, when creating a new ad set, its promoted_object. Attach requests do not change the existing ad set. Stored as creative.whatsappPhoneNumber on every created ad.'
|
|
917
920
|
headline:
|
|
918
921
|
type: string
|
|
919
922
|
minLength: 1
|
|
@@ -986,6 +989,9 @@ components:
|
|
|
986
989
|
type: string
|
|
987
990
|
pattern: '^\d+_\d+$'
|
|
988
991
|
description: 'Messaging and CTWA only. Raw Facebook pageId_postId reference, used as object_story_id even with an Instagram account. Mutually exclusive with existingPostId and fresh creative fields.'
|
|
992
|
+
creativeFeatures:
|
|
993
|
+
$ref: '#/components/schemas/MetaCreativeFeatures'
|
|
994
|
+
description: 'Replaces the top-level creativeFeatures map for this item. Omit to inherit; an empty object clears inherited enrollment choices.'
|
|
989
995
|
headline:
|
|
990
996
|
type: string
|
|
991
997
|
minLength: 1
|
|
@@ -8999,6 +9005,36 @@ components:
|
|
|
8999
9005
|
jobFunctions: { type: array, items: { type: string }, description: "LinkedIn B2B only." }
|
|
9000
9006
|
audienceInclude: { type: array, items: { type: string }, description: 'Platform audience IDs to include, as returned by GET /v1/ads/audiences (Meta custom audience ids, TikTok audience ids, Pinterest customer list ids, LinkedIn segment ids (the platformAudienceId from GET /v1/ads/audiences; Zernio resolves it to the targetable LinkedIn ad segment, an unknown id returns 400), Google user list ids, X custom audience ids). Not supported on OpenAI (400).' }
|
|
9001
9007
|
audienceExclude: { type: array, items: { type: string }, description: 'Platform audience IDs to exclude; same ID formats as audienceInclude. Not supported on OpenAI (400).' }
|
|
9008
|
+
MetaCreativeFeatures:
|
|
9009
|
+
type: object
|
|
9010
|
+
additionalProperties: { type: string, enum: [OPT_IN, OPT_OUT] }
|
|
9011
|
+
propertyNames: { pattern: '^[a-z0-9_]+$' }
|
|
9012
|
+
description: 'Meta Advantage+ creative enhancements. Map snake_case feature names to OPT_IN or OPT_OUT; Meta validates supported keys and unspecified features default to OPT_OUT. auto_promotion_tag is an enhancement; use the separate promotion field for an explicit offer. The deprecated standard_enhancements bundle is rejected by Meta.'
|
|
9013
|
+
example: { auto_promotion_tag: OPT_IN }
|
|
9014
|
+
MetaPromotion:
|
|
9015
|
+
type: [object, "null"]
|
|
9016
|
+
description: 'Meta explicit Promotion offer. Maps to creative_sourcing_spec.promotion_metadata_spec with promotion_source ADVERTISER_INPUT. Dates become Unix seconds. Send null to omit an explicit offer on a new creative or remove it when rebuilding. Creation success alone does not confirm application: inspect promotionStatus in the response.'
|
|
9017
|
+
required: [type, value]
|
|
9018
|
+
properties:
|
|
9019
|
+
type:
|
|
9020
|
+
type: string
|
|
9021
|
+
enum: [AMOUNT_OFF, FREE_RETURN, FREE_SHIPPING, PERCENTAGE_OFF, PROMO_CODE]
|
|
9022
|
+
description: 'Promotion type accepted by Meta. PERCENTAGE_OFF values cannot exceed 100.'
|
|
9023
|
+
value: { type: number, minimum: 0, description: 'Nonnegative promotion value passed to Meta unchanged. AMOUNT_OFF units are not confirmed, including major versus minor currency units. For PERCENTAGE_OFF this is the percentage discount, at most 100.' }
|
|
9024
|
+
code: { type: string, minLength: 1, description: 'Optional promotion code.' }
|
|
9025
|
+
startDate: { type: string, format: date-time, description: 'Optional ISO 8601 start timestamp with a timezone offset or Z.' }
|
|
9026
|
+
endDate: { type: string, format: date-time, description: 'Optional ISO 8601 end timestamp with a timezone offset or Z. Must be after startDate when both are set.' }
|
|
9027
|
+
example:
|
|
9028
|
+
type: PERCENTAGE_OFF
|
|
9029
|
+
value: 20
|
|
9030
|
+
code: SAVE20
|
|
9031
|
+
startDate: '2026-10-01T00:00:00Z'
|
|
9032
|
+
endDate: '2026-10-31T23:59:59Z'
|
|
9033
|
+
MetaPromotionStatus:
|
|
9034
|
+
type: string
|
|
9035
|
+
enum: [applied, not_returned, unavailable]
|
|
9036
|
+
description: 'Meta creative readback result. applied means Meta returned promotion metadata; not_returned means the read succeeded without promotion metadata; unavailable means the read failed. Only applied confirms the returned offer. Missing metadata is not proof that Ads Manager displays the requested Promotion.'
|
|
9037
|
+
example: not_returned
|
|
9002
9038
|
Ad:
|
|
9003
9039
|
type: object
|
|
9004
9040
|
properties:
|
|
@@ -9159,6 +9195,10 @@ components:
|
|
|
9159
9195
|
imageUrl: { type: string, description: Alternative image URL }
|
|
9160
9196
|
videoId: { type: [string, "null"], description: "Meta video ID for VIDEO-type ads. Null for non-video ads. Callers that need an embeddable MP4 can call GET /{videoId}?fields=source with the page access token." }
|
|
9161
9197
|
videoUrl: { type: [string, "null"], description: "Public Facebook watch URL for VIDEO-type ads (https://www.facebook.com/watch/?v={videoId}). Null for non-video ads." }
|
|
9198
|
+
promotion:
|
|
9199
|
+
$ref: '#/components/schemas/MetaPromotion'
|
|
9200
|
+
description: 'Meta offer read from the live creative on creation or GET /v1/ads/{adId}. Null when metadata is not returned or cannot be read. Requested values are never echoed as applied.'
|
|
9201
|
+
promotionStatus: { $ref: '#/components/schemas/MetaPromotionStatus' }
|
|
9162
9202
|
creativeId: { type: [string, "null"], description: "Meta ad creative id backing this ad. Reusable via existingCreativeId on POST /v1/ads/create." }
|
|
9163
9203
|
objectType: { type: string, description: "Meta creative object_type (e.g. SHARE, VIDEO, PRIVACY_CHECK_FAIL, POST_DELETED). Use this to render state-aware previews: when Meta moderation strips image/video fields, only thumbnailUrl at 64x64 is available." }
|
|
9164
9204
|
objectStoryId: { type: [string, "null"], description: "Meta creative `object_story_id` (the SHARE reference). Frequently absent, because Meta omits it for SHARE creatives. Use effectiveObjectStoryId instead." }
|
|
@@ -9188,6 +9228,7 @@ components:
|
|
|
9188
9228
|
googleHeadline: { type: string, description: Google Ads headline }
|
|
9189
9229
|
googleDescription: { type: string, description: Google Ads description }
|
|
9190
9230
|
linkUrl: { type: string, description: Destination URL }
|
|
9231
|
+
whatsappPhoneNumber: { type: string, description: 'Explicit E.164 WhatsApp number supplied when creating a Meta boost or messaging ad. Absent when omitted by the caller or on older records.', example: '+12025550123' }
|
|
9191
9232
|
pinterestImageUrl: { type: string }
|
|
9192
9233
|
pinterestTitle: { type: string }
|
|
9193
9234
|
pinterestDescription: { type: string }
|
|
@@ -43789,7 +43830,9 @@ paths:
|
|
|
43789
43830
|
Duplicates a single ad via Meta's native `POST /{ad-id}/copies`. The copy is created
|
|
43790
43831
|
paused. `adSetId` retargets the copy into another ad set; omitted = the source's own ad
|
|
43791
43832
|
set. Accepts the Zernio ad id or the platform ad id. Sync discovery is triggered
|
|
43792
|
-
automatically (`syncAfter: false` to skip).
|
|
43833
|
+
automatically (`syncAfter: false` to skip). Creative settings returned by Meta,
|
|
43834
|
+
including explicit promotion metadata and creativeFeatures, are preserved when the
|
|
43835
|
+
native copy requires a creative rebuild. Metadata Meta does not return cannot be recovered.
|
|
43793
43836
|
security:
|
|
43794
43837
|
- bearerAuth: []
|
|
43795
43838
|
parameters:
|
|
@@ -44281,9 +44324,19 @@ paths:
|
|
|
44281
44324
|
- the creative's `effective_instagram_media_id` (Instagram side)
|
|
44282
44325
|
|
|
44283
44326
|
Any of the four resolve to the same ad. Caller doesn't need a translation step.
|
|
44327
|
+
By default, creative.promotion and creative.creativeFeatures contain stored requested
|
|
44328
|
+
settings, which do not confirm platform application. With `refreshPromotion=true`,
|
|
44329
|
+
Meta promotion metadata is read live and exposed as `ad.creative.promotion`
|
|
44330
|
+
with `promotionStatus`. Only `applied` confirms an offer; `not_returned` means the
|
|
44331
|
+
creative read succeeded without promotion metadata, and `unavailable` means it failed.
|
|
44284
44332
|
security:
|
|
44285
44333
|
- bearerAuth: []
|
|
44286
44334
|
parameters:
|
|
44335
|
+
- name: refreshPromotion
|
|
44336
|
+
in: query
|
|
44337
|
+
required: false
|
|
44338
|
+
schema: { type: boolean, default: false }
|
|
44339
|
+
description: 'Meta only. Read current promotion metadata from Meta and include promotionStatus. Omit for stored creative settings with no promotion-specific Graph call.'
|
|
44287
44340
|
- name: adId
|
|
44288
44341
|
in: path
|
|
44289
44342
|
required: true
|
|
@@ -44299,6 +44352,7 @@ paths:
|
|
|
44299
44352
|
type: object
|
|
44300
44353
|
properties:
|
|
44301
44354
|
ad: { $ref: '#/components/schemas/Ad' }
|
|
44355
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
44302
44356
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
44303
44357
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
44304
44358
|
put:
|
|
@@ -44447,6 +44501,10 @@ paths:
|
|
|
44447
44501
|
GET /v1/ads/creatives and ignores every other field. Meta creatives are
|
|
44448
44502
|
immutable, so any change creates a new creative and repoints the ad; the old
|
|
44449
44503
|
creative is retained on the ad account for historical reporting.
|
|
44504
|
+
`promotion` and `creativeFeatures` are Meta-only. Omitted settings are
|
|
44505
|
+
preserved from the live creative, including full rebuilds. Send
|
|
44506
|
+
`promotion: null` to remove the explicit offer from the replacement.
|
|
44507
|
+
A supplied creativeFeatures map overrides individual existing keys.
|
|
44450
44508
|
- **TikTok**: patch-style. Pass any subset; `headline` is ignored (TikTok creatives
|
|
44451
44509
|
have no headline slot). `body` becomes the in-feed `ad_text`; `linkUrl` becomes
|
|
44452
44510
|
`landing_page_url`; `videoUrl` triggers a fresh upload. `description`, `videoId`
|
|
@@ -44457,6 +44515,8 @@ paths:
|
|
|
44457
44515
|
The old creative is retained for historical reporting. `videoId` and
|
|
44458
44516
|
`existingCreativeId` are Meta-only and return 400.
|
|
44459
44517
|
properties:
|
|
44518
|
+
promotion: { $ref: '#/components/schemas/MetaPromotion' }
|
|
44519
|
+
creativeFeatures: { $ref: '#/components/schemas/MetaCreativeFeatures' }
|
|
44460
44520
|
headline: { type: string, description: "Meta and LinkedIn (TikTok has no headline slot)" }
|
|
44461
44521
|
body: { type: string }
|
|
44462
44522
|
description: { type: string, maxLength: 255, description: "Link description slot (Meta `link_data.description` / `video_data.link_description`, LinkedIn creative description)." }
|
|
@@ -44467,6 +44527,10 @@ paths:
|
|
|
44467
44527
|
videoId: { type: string, description: "Meta only. Reuse an already-uploaded ad video (from POST /v1/ads/videos or GET /v1/ads/videos) instead of re-uploading via videoUrl." }
|
|
44468
44528
|
existingCreativeId: { type: string, description: "Meta only. Repoint the ad at an existing library creative (from GET /v1/ads/creatives); all other creative fields are ignored." }
|
|
44469
44529
|
name: { type: string, maxLength: 255, description: "Rename the ad. Now propagated to Meta (POST /{ad-id}); non-Meta platforms return 501." }
|
|
44530
|
+
example:
|
|
44531
|
+
creative:
|
|
44532
|
+
promotion: null
|
|
44533
|
+
creativeFeatures: { auto_promotion_tag: OPT_OUT }
|
|
44470
44534
|
responses:
|
|
44471
44535
|
'200':
|
|
44472
44536
|
description: Ad updated
|
|
@@ -45885,7 +45949,11 @@ paths:
|
|
|
45885
45949
|
`existingCreativeId`. Provide exactly one of `imageUrl` (uploaded server-side),
|
|
45886
45950
|
`imageHash` (from POST /v1/ads/images or the library list), or `carouselCards` (2-10
|
|
45887
45951
|
hand-built cards). The Page (and linked Instagram account, when present) is resolved
|
|
45888
|
-
from `accountId` as the story actor.
|
|
45952
|
+
from `accountId` as the story actor. `promotion` configures an explicit offer separately
|
|
45953
|
+
from Advantage+ `creativeFeatures`. Only when `promotion` is supplied does the response
|
|
45954
|
+
read the creative back from Meta;
|
|
45955
|
+
`promotionStatus: not_returned` means Meta accepted creation but omitted promotion
|
|
45956
|
+
metadata, so the requested offer is not confirmed as applied.
|
|
45889
45957
|
security:
|
|
45890
45958
|
- bearerAuth: []
|
|
45891
45959
|
requestBody:
|
|
@@ -45919,14 +45987,23 @@ paths:
|
|
|
45919
45987
|
description: { type: string, maxLength: 255 }
|
|
45920
45988
|
callToAction: { type: string }
|
|
45921
45989
|
urlTags: { type: string, description: "Appended to every outbound URL (e.g. utm_source=fb)." }
|
|
45990
|
+
promotion: { $ref: '#/components/schemas/MetaPromotion' }
|
|
45922
45991
|
creativeFeatures:
|
|
45923
|
-
|
|
45924
|
-
|
|
45925
|
-
description: 'Advantage+ creative enhancements: partial map of Meta creative feature keys (snake_case) to enroll status, forwarded as degrees_of_freedom_spec.creative_features_spec. Unspecified features default to OPT_OUT.'
|
|
45992
|
+
$ref: '#/components/schemas/MetaCreativeFeatures'
|
|
45993
|
+
description: 'Meta only. Applied to each new creative, including standalone and attach shapes. With creatives[], these are defaults; an item replaces the whole feature map, including an empty map. auto_promotion_tag is an enhancement; an explicit offer uses promotion.'
|
|
45926
45994
|
multiAdvertiser:
|
|
45927
45995
|
type: string
|
|
45928
45996
|
enum: [OPT_IN, OPT_OUT]
|
|
45929
45997
|
description: "Meta only. Multi-advertiser ads: whether Meta may show this ad alongside other advertisers' in one unit. Meta auto-enrols since Aug 2024, so send OPT_OUT to leave. It is a top-level creative field, NOT a `creativeFeatures` key, and Meta rejects it there."
|
|
45998
|
+
example:
|
|
45999
|
+
accountId: '69fc524892b3d8e85f893e73'
|
|
46000
|
+
adAccountId: act_123456789
|
|
46001
|
+
headline: Save on your next order
|
|
46002
|
+
body: Use SAVE20 at checkout.
|
|
46003
|
+
linkUrl: https://example.com/shop
|
|
46004
|
+
imageUrl: https://example.com/ad.jpg
|
|
46005
|
+
promotion: { type: PERCENTAGE_OFF, value: 20, code: SAVE20 }
|
|
46006
|
+
creativeFeatures: { auto_promotion_tag: OPT_OUT }
|
|
45930
46007
|
responses:
|
|
45931
46008
|
'201':
|
|
45932
46009
|
description: Creative created
|
|
@@ -45937,6 +46014,13 @@ paths:
|
|
|
45937
46014
|
properties:
|
|
45938
46015
|
adAccountId: { type: string }
|
|
45939
46016
|
creativeId: { type: string, description: "Platform creative id, reusable via existingCreativeId." }
|
|
46017
|
+
promotion: { $ref: '#/components/schemas/MetaPromotion' }
|
|
46018
|
+
promotionStatus: { $ref: '#/components/schemas/MetaPromotionStatus' }
|
|
46019
|
+
example:
|
|
46020
|
+
adAccountId: act_123456789
|
|
46021
|
+
creativeId: '123456789012345'
|
|
46022
|
+
promotion: null
|
|
46023
|
+
promotionStatus: not_returned
|
|
45940
46024
|
'400': { description: "Invalid input, or Meta rejected the create" }
|
|
45941
46025
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
45942
46026
|
'422': { description: No Facebook Page found to act as the story actor }
|
|
@@ -47453,7 +47537,9 @@ paths:
|
|
|
47453
47537
|
**Messaging boosts (Meta).** Use `goal: engagement` with
|
|
47454
47538
|
`callToAction: WHATSAPP_MESSAGE`, `MESSAGE_PAGE`, or `INSTAGRAM_MESSAGE`.
|
|
47455
47539
|
The CTA implies WHATSAPP, MESSENGER, or INSTAGRAM_DIRECT respectively;
|
|
47456
|
-
`destinationType` alone
|
|
47540
|
+
`destinationType` alone does not select a messaging CTA. Omit `linkUrl`
|
|
47541
|
+
only for messaging CTAs. Plain link CTAs keep their goal and link behavior
|
|
47542
|
+
when combined with an independent `destinationType`.
|
|
47457
47543
|
The campaign uses OUTCOME_ENGAGEMENT and the ad set uses CONVERSATIONS
|
|
47458
47544
|
with the promoted Page. Optional `whatsappPhoneNumber` selects a number
|
|
47459
47545
|
already paired with that Page. Conflicting CTA/destination, instant form,
|
|
@@ -47483,6 +47569,7 @@ paths:
|
|
|
47483
47569
|
type: object
|
|
47484
47570
|
required: [accountId, adAccountId, name, goal]
|
|
47485
47571
|
properties:
|
|
47572
|
+
creativeFeatures: { $ref: '#/components/schemas/MetaCreativeFeatures' }
|
|
47486
47573
|
postId: { type: string, description: Zernio post ID (provide this or platformPostId) }
|
|
47487
47574
|
platformPostId: { type: string, description: Platform post ID (alternative to postId) }
|
|
47488
47575
|
accountId: { type: string, description: Account ID }
|
|
@@ -47498,8 +47585,8 @@ paths:
|
|
|
47498
47585
|
amount: { type: number, description: "Minimum varies: TikTok=$20, Pinterest=$5, others=$1" }
|
|
47499
47586
|
type: { type: string, enum: [daily, lifetime] }
|
|
47500
47587
|
instagramAccountId: { type: string, description: "Meta only. Instagram identity the ad runs AS (creative.instagram_user_id), overriding the account linked to the Page. Live-verified against a Page-post creative." }
|
|
47501
|
-
destinationType: { type: string, enum: [INSTAGRAM_PROFILE, WEBSITE, ON_AD, MESSENGER, WHATSAPP, INSTAGRAM_DIRECT], description: "Meta only. Ad-set destination_type: where the click LANDS, as opposed to instagramAccountId which is who the ad runs as.
|
|
47502
|
-
whatsappPhoneNumber: { type: string, pattern: '^\+[1-9]\d{6,14}$', description: 'Meta WhatsApp only. E.164 number already paired with the Page. Omit to use the default pairing. Requires
|
|
47588
|
+
destinationType: { type: string, enum: [INSTAGRAM_PROFILE, WEBSITE, ON_AD, MESSENGER, WHATSAPP, INSTAGRAM_DIRECT], description: "Meta only. Ad-set destination_type: where the click LANDS, as opposed to instagramAccountId which is who the ad runs as. Independent of plain link CTAs and their goal. A messaging callToAction selects its destination automatically; an explicit destinationType must then match. Lead ads use ON_AD." }
|
|
47589
|
+
whatsappPhoneNumber: { type: string, pattern: '^\+[1-9]\d{6,14}$', description: 'Meta WhatsApp only. E.164 number already paired with the Page. Omit to use the default pairing. Requires WHATSAPP_MESSAGE callToAction. Stored as creative.whatsappPhoneNumber on the ad.' }
|
|
47503
47590
|
currency: { type: string, minLength: 3, maxLength: 3, example: USD, description: "ISO 4217 currency code matching the ad account's currency. Meta only. Optional: Zernio resolves it from the ad account when omitted. The value selects the minor-unit exponent Zernio converts budget/bid amounts by before calling Meta (most currencies are cents; zero-decimal currencies like JPY/KRW are sent as-is)." }
|
|
47504
47591
|
schedule:
|
|
47505
47592
|
type: object
|
|
@@ -47734,7 +47821,7 @@ paths:
|
|
|
47734
47821
|
status:
|
|
47735
47822
|
type: string
|
|
47736
47823
|
enum: [ACTIVE, PAUSED]
|
|
47737
|
-
description: 'Meta, TikTok, and LinkedIn. Publish state of the created entities. Omitted or ACTIVE publishes live (default); PAUSED creates them paused so you can review before they spend. On LinkedIn the whole campaign group, campaign, and creative hierarchy stays PAUSED (intendedStatus PAUSED on each).'
|
|
47824
|
+
description: 'Meta, TikTok, and LinkedIn. Publish state of the created entities. Omitted or ACTIVE publishes live (default); PAUSED creates them paused so you can review before they spend. On Meta a new campaign stays paused until explicitly activated; an attached ad is itself paused. On LinkedIn the whole campaign group, campaign, and creative hierarchy stays PAUSED (intendedStatus PAUSED on each).'
|
|
47738
47825
|
optimizationGoal:
|
|
47739
47826
|
type: string
|
|
47740
47827
|
description: |
|
|
@@ -47754,6 +47841,7 @@ paths:
|
|
|
47754
47841
|
name: WhatsApp post boost
|
|
47755
47842
|
goal: engagement
|
|
47756
47843
|
callToAction: WHATSAPP_MESSAGE
|
|
47844
|
+
whatsappPhoneNumber: '+12025550123'
|
|
47757
47845
|
budget: { amount: 2.61, type: daily }
|
|
47758
47846
|
status: PAUSED
|
|
47759
47847
|
responses:
|
|
@@ -47801,6 +47889,16 @@ paths:
|
|
|
47801
47889
|
- Meta-only multi-creative shape via the creatives array: one ad set with N ads sharing budget and targeting.
|
|
47802
47890
|
- 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.
|
|
47803
47891
|
|
|
47892
|
+
Meta accepts `promotion` and `creativeFeatures` on the single and attach shapes and
|
|
47893
|
+
as defaults for `creatives[]`. An item replaces the whole feature map; its `promotion`
|
|
47894
|
+
replaces the default offer, and `promotion: null` disables that default for the item.
|
|
47895
|
+
Reusing `existingCreativeId` uses the existing creative settings instead of new settings.
|
|
47896
|
+
Requested settings are persisted for lists, exports, and default ad-detail reads.
|
|
47897
|
+
Only ads supplied a `promotion` receive live readback; multi-create batches those reads
|
|
47898
|
+
in groups of up to 50 IDs without per-ad fallback. Inspect `ad.creative.promotionStatus` (or
|
|
47899
|
+
`ads[].creative.promotionStatus`). `not_returned` means Meta omitted the metadata;
|
|
47900
|
+
successful creation does not by itself prove the offer was applied or will display.
|
|
47901
|
+
|
|
47804
47902
|
Per-platform required fields, budget minimums, and video-ad rules are documented on each property below.
|
|
47805
47903
|
|
|
47806
47904
|
LinkedIn creates a Single Image or Single Video Ad backed by a Direct Sponsored Content "dark post" authored by a Company Page (see `organizationId`). Supported goals are engagement, traffic, awareness, and video_views (video ads use the `video` field; video_views requires a video), and traffic ads require `linkUrl`.
|
|
@@ -47867,10 +47965,10 @@ paths:
|
|
|
47867
47965
|
billingEvent: { type: string, description: "Meta only. Explicit ad-set `billing_event`. Defaults to `IMPRESSIONS`. Forwarded verbatim to Meta, which validates compatibility with the optimization goal." }
|
|
47868
47966
|
buyingType: { type: string, enum: [AUCTION, RESERVED], description: "Meta only. RESERVED = Reach & Frequency: requires `rfPredictionId` (a RESERVED prediction from /v1/ads/rf-predictions + /reserve). Budget, schedule and pricing come from the reservation, so budgetAmount/budgetType are not required and bid fields are ignored. Only the plain single-ad shape (no creatives[], adSetId, existingCampaignId or dynamicCreative)." }
|
|
47869
47967
|
rfPredictionId: { type: string, description: "Meta only. The RESERVED prediction id the R&F ad set runs on (reserving mints a new id, so pass that one). Requires buyingType RESERVED." }
|
|
47968
|
+
promotion: { $ref: '#/components/schemas/MetaPromotion' }
|
|
47870
47969
|
creativeFeatures:
|
|
47871
|
-
|
|
47872
|
-
|
|
47873
|
-
description: 'Meta only. Advantage+ creative enhancements: a partial map of Meta creative feature keys (snake_case, e.g. enhance_cta, image_brightness_and_contrast, text_optimizations) to enroll status, forwarded as degrees_of_freedom_spec.creative_features_spec. Meta validates the keys; unspecified features default to OPT_OUT. The legacy standard_enhancements bundle is deprecated by Meta and rejected.'
|
|
47970
|
+
$ref: '#/components/schemas/MetaCreativeFeatures'
|
|
47971
|
+
description: 'Meta only. Applied to each new creative, including standalone and attach shapes. With creatives[], these are defaults; an item replaces the whole feature map, including an empty map. auto_promotion_tag is an enhancement; an explicit offer uses promotion.'
|
|
47874
47972
|
multiAdvertiser:
|
|
47875
47973
|
type: string
|
|
47876
47974
|
enum: [OPT_IN, OPT_OUT]
|
|
@@ -47966,6 +48064,12 @@ paths:
|
|
|
47966
48064
|
required: [headline, body, linkUrl, callToAction]
|
|
47967
48065
|
description: "Each creative must supply EXACTLY ONE of `imageUrl` (image creative) or `video` (video creative)."
|
|
47968
48066
|
properties:
|
|
48067
|
+
promotion:
|
|
48068
|
+
$ref: '#/components/schemas/MetaPromotion'
|
|
48069
|
+
description: 'Overrides the top-level offer for this item. Omit to inherit; null disables the inherited offer.'
|
|
48070
|
+
creativeFeatures:
|
|
48071
|
+
$ref: '#/components/schemas/MetaCreativeFeatures'
|
|
48072
|
+
description: 'Replaces the entire top-level creativeFeatures map for this item. Omit to inherit; an empty map clears these defaults.'
|
|
47969
48073
|
name: { type: string, maxLength: 255, description: "Exact name for this ad. Falls back to `<name> #N` (N = 1-based position)." }
|
|
47970
48074
|
headline: { type: string, maxLength: 255 }
|
|
47971
48075
|
body: { type: string }
|
|
@@ -47989,7 +48093,8 @@ paths:
|
|
|
47989
48093
|
are inherited from the ad set on Meta, and passing `bidStrategy`
|
|
47990
48094
|
in attach mode returns 400. To change an existing ad set's
|
|
47991
48095
|
bid, use `PUT /v1/ads/ad-sets/{adSetId}`. Mutually exclusive
|
|
47992
|
-
with `creatives[]`.
|
|
48096
|
+
with `creatives[]`. `dynamicCreative` returns 400 in attach mode: create
|
|
48097
|
+
a new dynamic ad set by omitting `adSetId` instead.
|
|
47993
48098
|
|
|
47994
48099
|
The attached ad takes the full single-creative surface:
|
|
47995
48100
|
`headline`/`body`/`description`/`callToAction` plus either
|
|
@@ -48296,7 +48401,10 @@ paths:
|
|
|
48296
48401
|
(`imageUrl`, `headline`, `body`, `linkUrl`, `callToAction`) are ignored. Mutually
|
|
48297
48402
|
exclusive with the `creatives[]` multi-creative shape. Exactly ONE of `imageUrls` /
|
|
48298
48403
|
`videoUrls` is required (Meta allows one ad format per asset feed; sending both →
|
|
48299
|
-
400).
|
|
48404
|
+
400). Limits remain 10 images or videos and 5 bodies, titles or descriptions.
|
|
48405
|
+
The ad set is created with `is_dynamic_creative: true`. Combining this field
|
|
48406
|
+
with `adSetId` returns 400: omit `adSetId` to create a new dynamic ad set.
|
|
48407
|
+
Multiple headlines go in `titles`; multiple primary texts go in `bodies`.
|
|
48300
48408
|
properties:
|
|
48301
48409
|
imageUrls:
|
|
48302
48410
|
type: array
|
|
@@ -48836,10 +48944,10 @@ paths:
|
|
|
48836
48944
|
error_subcode 1885014).
|
|
48837
48945
|
productCatalogId:
|
|
48838
48946
|
type: string
|
|
48839
|
-
description:
|
|
48947
|
+
description: "Optional catalog ID. If supplied with productSetId, the set must belong to this catalog. A catalog ID cannot replace productSetId."
|
|
48840
48948
|
productSetId:
|
|
48841
48949
|
type: string
|
|
48842
|
-
description:
|
|
48950
|
+
description: "Meta product SET ID from GET /v1/ads/catalogs/{catalogId}/product-sets. Zernio checks that the token can read the set and its product_catalog before creation. A catalog ID or inaccessible set returns a precise 400 naming promotedObject.productSetId. A mismatch with productCatalogId names promotedObject.productCatalogId."
|
|
48843
48951
|
offlineConversionDataSetId:
|
|
48844
48952
|
type: string
|
|
48845
48953
|
description: 'Meta only. Offline event set (dataset) to optimise toward. Post-merger these are datasets: the id is the dataset id (for pixel-backed datasets, the pixel id).'
|
|
@@ -48847,6 +48955,21 @@ paths:
|
|
|
48847
48955
|
type: string
|
|
48848
48956
|
description: 'Meta only. WhatsApp number on messaging-destination ad sets.'
|
|
48849
48957
|
additionalProperties: false
|
|
48958
|
+
example:
|
|
48959
|
+
accountId: '69fc524892b3d8e85f893e73'
|
|
48960
|
+
adAccountId: act_123456789
|
|
48961
|
+
name: Autumn promotion
|
|
48962
|
+
goal: traffic
|
|
48963
|
+
budgetAmount: 5
|
|
48964
|
+
budgetType: daily
|
|
48965
|
+
status: PAUSED
|
|
48966
|
+
headline: Save on your next order
|
|
48967
|
+
body: Use SAVE20 at checkout.
|
|
48968
|
+
callToAction: SHOP_NOW
|
|
48969
|
+
linkUrl: https://example.com/shop
|
|
48970
|
+
imageUrl: https://example.com/ad.jpg
|
|
48971
|
+
promotion: { type: PERCENTAGE_OFF, value: 20, code: SAVE20 }
|
|
48972
|
+
creativeFeatures: { auto_promotion_tag: OPT_OUT }
|
|
48850
48973
|
responses:
|
|
48851
48974
|
'200':
|
|
48852
48975
|
description: 'validateOnly dry-run passed, nothing was created'
|
|
@@ -50013,7 +50136,7 @@ paths:
|
|
|
50013
50136
|
tags: ["Ad Creatives"]
|
|
50014
50137
|
x-platforms: ["meta"]
|
|
50015
50138
|
summary: List a catalog's product sets
|
|
50016
|
-
description: "Lists a Meta product catalog's product sets, the unit a catalog ad promotes. Pass the chosen set as `promotedObject.productSetId` on POST /v1/ads/create with `goal: catalog_sales`."
|
|
50139
|
+
description: "Lists a Meta product catalog's product sets, the unit a catalog ad promotes. Pass the chosen set id, not the parent catalog id, as `promotedObject.productSetId` on POST /v1/ads/create with `goal: catalog_sales`. Creation verifies set visibility and returns 400 for a catalog id or an inaccessible set."
|
|
50017
50140
|
security:
|
|
50018
50141
|
- bearerAuth: []
|
|
50019
50142
|
parameters:
|
|
@@ -59,7 +59,7 @@ describe 'AdCampaignsApi' do
|
|
|
59
59
|
|
|
60
60
|
# unit tests for boost_post
|
|
61
61
|
# Boost post as ad
|
|
62
|
-
# Creates a paid ad from an existing published post, keeping the post's engagement. By default it provisions the whole hierarchy (campaign, ad set, ad). **Attach shape (Meta).** Send `adSetId` to put the ad under an EXISTING ad set instead, so that ad set keeps its learning phase. It then owns `budget`, `schedule` and `targeting`, and sending any of those alongside `adSetId` is a 400 rather than a silent drop. `budget` is required only without `adSetId`. `instagramAccountId`, `destinationType`, `whatsappPhoneNumber` and `adSetId` are Meta-only and return 400 on other platforms. **Messaging boosts (Meta).** Use `goal: engagement` with `callToAction: WHATSAPP_MESSAGE`, `MESSAGE_PAGE`, or `INSTAGRAM_MESSAGE`. The CTA implies WHATSAPP, MESSENGER, or INSTAGRAM_DIRECT respectively; `destinationType` alone
|
|
62
|
+
# Creates a paid ad from an existing published post, keeping the post's engagement. By default it provisions the whole hierarchy (campaign, ad set, ad). **Attach shape (Meta).** Send `adSetId` to put the ad under an EXISTING ad set instead, so that ad set keeps its learning phase. It then owns `budget`, `schedule` and `targeting`, and sending any of those alongside `adSetId` is a 400 rather than a silent drop. `budget` is required only without `adSetId`. `instagramAccountId`, `destinationType`, `whatsappPhoneNumber` and `adSetId` are Meta-only and return 400 on other platforms. **Messaging boosts (Meta).** Use `goal: engagement` with `callToAction: WHATSAPP_MESSAGE`, `MESSAGE_PAGE`, or `INSTAGRAM_MESSAGE`. The CTA implies WHATSAPP, MESSENGER, or INSTAGRAM_DIRECT respectively; `destinationType` alone does not select a messaging CTA. Omit `linkUrl` only for messaging CTAs. Plain link CTAs keep their goal and link behavior when combined with an independent `destinationType`. The campaign uses OUTCOME_ENGAGEMENT and the ad set uses CONVERSATIONS with the promoted Page. Optional `whatsappPhoneNumber` selects a number already paired with that Page. Conflicting CTA/destination, instant form, goal, or optimizationGoal inputs return 400. Attach requires the target ad set destination to match. Existing post references preserve social proof; an Instagram reel rejected by Meta is not re-uploaded as a new post for a messaging boost. **Retries.** Boosts are NOT idempotent and can take minutes when Meta requires re-hosting an Instagram video, so do not retry on client timeout. Send an Idempotency-Key header to make retries safe: same key and body replays the original 201, and distinct keys always create distinct ads. Without the header, an identical request is treated as a retry: while one is in flight it returns 409, and within 10 minutes of a completed boost it returns the already-created ad instead of creating another. To intentionally duplicate an ad, send distinct Idempotency-Keys (or vary the body, e.g. the name).
|
|
63
63
|
# @param boost_post_request
|
|
64
64
|
# @param [Hash] opts the optional parameters
|
|
65
65
|
# @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.
|
|
@@ -122,7 +122,7 @@ describe 'AdCampaignsApi' do
|
|
|
122
122
|
|
|
123
123
|
# unit tests for create_standalone_ad
|
|
124
124
|
# Create standalone ad
|
|
125
|
-
# Create a paid ad with custom creative across Meta, Google Ads, Pinterest, TikTok, X, LinkedIn, and OpenAI Ads (ChatGPT Ads). Three 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. 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 \"dark post\" authored by a Company Page (see `organizationId`). Supported goals are engagement, traffic, awareness, and video_views (video ads use the `video` field; video_views requires a video), and traffic ads require `linkUrl`. **Idempotency:** this endpoint is not idempotent at the platform level (a blind retry creates a second campaign/ad set/ad). Send an `Idempotency-Key` 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 `Idempotent-Replayed: true`) 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.
|
|
125
|
+
# Create a paid ad with custom creative across Meta, Google Ads, Pinterest, TikTok, X, LinkedIn, and OpenAI Ads (ChatGPT Ads). Three 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 `promotion` and `creativeFeatures` on the single and attach shapes and as defaults for `creatives[]`. An item replaces the whole feature map; its `promotion` replaces the default offer, and `promotion: null` disables that default for the item. Reusing `existingCreativeId` 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 `promotion` receive live readback; multi-create batches those reads in groups of up to 50 IDs without per-ad fallback. Inspect `ad.creative.promotionStatus` (or `ads[].creative.promotionStatus`). `not_returned` 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 \"dark post\" authored by a Company Page (see `organizationId`). Supported goals are engagement, traffic, awareness, and video_views (video ads use the `video` field; video_views requires a video), and traffic ads require `linkUrl`. **Idempotency:** this endpoint is not idempotent at the platform level (a blind retry creates a second campaign/ad set/ad). Send an `Idempotency-Key` 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 `Idempotent-Replayed: true`) 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.
|
|
126
126
|
# @param create_standalone_ad_request
|
|
127
127
|
# @param [Hash] opts the optional parameters
|
|
128
128
|
# @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.
|
|
@@ -172,7 +172,7 @@ describe 'AdCampaignsApi' do
|
|
|
172
172
|
|
|
173
173
|
# unit tests for duplicate_ad
|
|
174
174
|
# Duplicate an ad
|
|
175
|
-
# Duplicates a single ad via Meta's native `POST /{ad-id}/copies`. The copy is created paused. `adSetId` retargets the copy into another ad set; omitted = the source's own ad set. Accepts the Zernio ad id or the platform ad id. Sync discovery is triggered automatically (`syncAfter: false` to skip).
|
|
175
|
+
# Duplicates a single ad via Meta's native `POST /{ad-id}/copies`. The copy is created paused. `adSetId` retargets the copy into another ad set; omitted = the source's own ad set. Accepts the Zernio ad id or the platform ad id. Sync discovery is triggered automatically (`syncAfter: false` to skip). Creative settings returned by Meta, including explicit promotion metadata and creativeFeatures, are preserved when the native copy requires a creative rebuild. Metadata Meta does not return cannot be recovered.
|
|
176
176
|
# @param ad_id Zernio ad ID or platform ad ID
|
|
177
177
|
# @param [Hash] opts the optional parameters
|
|
178
178
|
# @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. Only 2xx responses are stored, so a request that failed with a 4xx can be retried with a corrected body under the SAME key.
|
|
@@ -214,9 +214,10 @@ describe 'AdCampaignsApi' do
|
|
|
214
214
|
|
|
215
215
|
# unit tests for get_ad
|
|
216
216
|
# Get ad details
|
|
217
|
-
# Returns an ad with its creative, targeting, status, and performance metrics. The `{adId}` path segment accepts any identifier dialect Zernio indexes for the ad: - the Zernio internal `_id` (24-char hex) - Meta's numeric `platformAdId` (the value shipped in `comment.received` webhooks as `comment.ad.id`) - the creative's `effective_object_story_id` (`{pageId}_{postId}` shape, Facebook side) - the creative's `effective_instagram_media_id` (Instagram side) Any of the four resolve to the same ad. Caller doesn't need a translation step.
|
|
217
|
+
# Returns an ad with its creative, targeting, status, and performance metrics. The `{adId}` path segment accepts any identifier dialect Zernio indexes for the ad: - the Zernio internal `_id` (24-char hex) - Meta's numeric `platformAdId` (the value shipped in `comment.received` webhooks as `comment.ad.id`) - the creative's `effective_object_story_id` (`{pageId}_{postId}` shape, Facebook side) - the creative's `effective_instagram_media_id` (Instagram side) Any of the four resolve to the same ad. Caller doesn't need a translation step. By default, creative.promotion and creative.creativeFeatures contain stored requested settings, which do not confirm platform application. With `refreshPromotion=true`, Meta promotion metadata is read live and exposed as `ad.creative.promotion` with `promotionStatus`. Only `applied` confirms an offer; `not_returned` means the creative read succeeded without promotion metadata, and `unavailable` means it failed.
|
|
218
218
|
# @param ad_id Zernio `_id` (hex), Meta `platformAdId` (numeric), or one of the creative's effective story/media IDs. See description for details.
|
|
219
219
|
# @param [Hash] opts the optional parameters
|
|
220
|
+
# @option opts [Boolean] :refresh_promotion Meta only. Read current promotion metadata from Meta and include promotionStatus. Omit for stored creative settings with no promotion-specific Graph call.
|
|
220
221
|
# @return [GetAd200Response]
|
|
221
222
|
describe 'get_ad test' do
|
|
222
223
|
it 'should work' do
|
|
@@ -34,7 +34,7 @@ describe 'AdCreativesApi' do
|
|
|
34
34
|
|
|
35
35
|
# unit tests for create_ad_creative
|
|
36
36
|
# Create a standalone creative
|
|
37
|
-
# Creates a creative in the library WITHOUT an ad, reusable on the create endpoints via `existingCreativeId`. Provide exactly one of `imageUrl` (uploaded server-side), `imageHash` (from POST /v1/ads/images or the library list), or `carouselCards` (2-10 hand-built cards). The Page (and linked Instagram account, when present) is resolved from `accountId` as the story actor.
|
|
37
|
+
# Creates a creative in the library WITHOUT an ad, reusable on the create endpoints via `existingCreativeId`. Provide exactly one of `imageUrl` (uploaded server-side), `imageHash` (from POST /v1/ads/images or the library list), or `carouselCards` (2-10 hand-built cards). The Page (and linked Instagram account, when present) is resolved from `accountId` as the story actor. `promotion` configures an explicit offer separately from Advantage+ `creativeFeatures`. Only when `promotion` is supplied does the response read the creative back from Meta; `promotionStatus: not_returned` means Meta accepted creation but omitted promotion metadata, so the requested offer is not confirmed as applied.
|
|
38
38
|
# @param create_ad_creative_request
|
|
39
39
|
# @param [Hash] opts the optional parameters
|
|
40
40
|
# @return [CreateAdCreative201Response]
|
|
@@ -124,7 +124,7 @@ describe 'AdCreativesApi' do
|
|
|
124
124
|
|
|
125
125
|
# unit tests for list_ad_catalog_product_sets
|
|
126
126
|
# List a catalog's product sets
|
|
127
|
-
# Lists a Meta product catalog's product sets, the unit a catalog ad promotes. Pass the chosen set as `promotedObject.productSetId` on POST /v1/ads/create with `goal: catalog_sales`.
|
|
127
|
+
# Lists a Meta product catalog's product sets, the unit a catalog ad promotes. Pass the chosen set id, not the parent catalog id, as `promotedObject.productSetId` on POST /v1/ads/create with `goal: catalog_sales`. Creation verifies set visibility and returns 400 for a catalog id or an inaccessible set.
|
|
128
128
|
# @param catalog_id Meta product catalog ID (from GET /v1/ads/catalogs)
|
|
129
129
|
# @param account_id A facebook, instagram, or metaads account ID
|
|
130
130
|
# @param [Hash] opts the optional parameters
|
|
@@ -51,6 +51,18 @@ describe Zernio::AdCreative do
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
describe 'test attribute "promotion"' do
|
|
55
|
+
it 'should work' do
|
|
56
|
+
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe 'test attribute "promotion_status"' do
|
|
61
|
+
it 'should work' do
|
|
62
|
+
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
54
66
|
describe 'test attribute "creative_id"' do
|
|
55
67
|
it 'should work' do
|
|
56
68
|
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
|
|
@@ -141,6 +153,12 @@ describe Zernio::AdCreative do
|
|
|
141
153
|
end
|
|
142
154
|
end
|
|
143
155
|
|
|
156
|
+
describe 'test attribute "whatsapp_phone_number"' do
|
|
157
|
+
it 'should work' do
|
|
158
|
+
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
144
162
|
describe 'test attribute "pinterest_image_url"' do
|
|
145
163
|
it 'should work' do
|
|
146
164
|
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
|
|
@@ -27,6 +27,16 @@ describe Zernio::BoostPostRequest do
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
describe 'test attribute "creative_features"' do
|
|
31
|
+
it 'should work' do
|
|
32
|
+
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
|
|
33
|
+
# validator = Petstore::EnumTest::EnumAttributeValidator.new('Hash<String, String>', ["OPT_IN", "OPT_OUT"])
|
|
34
|
+
# validator.allowable_values.each do |value|
|
|
35
|
+
# expect { instance.creative_features = value }.not_to raise_error
|
|
36
|
+
# end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
30
40
|
describe 'test attribute "post_id"' do
|
|
31
41
|
it 'should work' do
|
|
32
42
|
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
|
|
@@ -39,4 +39,16 @@ describe Zernio::CreateAdCreative201Response do
|
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
describe 'test attribute "promotion"' do
|
|
43
|
+
it 'should work' do
|
|
44
|
+
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe 'test attribute "promotion_status"' do
|
|
49
|
+
it 'should work' do
|
|
50
|
+
# assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
42
54
|
end
|