late-sdk 0.0.899 → 0.0.900

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -2
  3. data/docs/AdAccountsApi.md +463 -3
  4. data/docs/AdCampaignsApi.md +4 -4
  5. data/docs/AdTracking.md +20 -0
  6. data/docs/CreateAdCampaign200Response.md +26 -0
  7. data/docs/CreateAdCampaignRequest.md +8 -0
  8. data/docs/CreateCallAdRequest.md +2 -0
  9. data/docs/CreateMessagingAdRequest.md +2 -0
  10. data/docs/CreateStandaloneAdRequest.md +11 -3
  11. data/docs/CtwaAdRequestBody.md +2 -0
  12. data/docs/GetAdComments200ResponseMeta.md +10 -4
  13. data/docs/GetIosFourteenCampaignLimits200Response.md +18 -0
  14. data/docs/GetIosFourteenCampaignLimits200ResponseLimits.md +22 -0
  15. data/docs/HideAdComment200Response.md +22 -0
  16. data/docs/HideAdCommentRequest.md +18 -0
  17. data/docs/ListAdsInstagramAccounts200Response.md +22 -0
  18. data/docs/ListAdsInstagramAccounts200ResponseAccountsInner.md +26 -0
  19. data/docs/ListAdsInstagramAccounts200ResponsePagesInner.md +24 -0
  20. data/docs/ListAdsInstagramAccounts200ResponseResolved.md +22 -0
  21. data/docs/ListAdvertisableApplications200Response.md +18 -0
  22. data/docs/ListAdvertisableApplications200ResponseApplicationsInner.md +24 -0
  23. data/docs/MetaInstagramIdentityRef.md +22 -0
  24. data/docs/ReplyToAdComment200Response.md +20 -0
  25. data/docs/ReplyToAdCommentRequest.md +18 -0
  26. data/docs/TargetingSpec.md +4 -0
  27. data/lib/zernio-sdk/api/ad_accounts_api.rb +512 -4
  28. data/lib/zernio-sdk/api/ad_campaigns_api.rb +3 -3
  29. data/lib/zernio-sdk/models/{create_standalone_ad_request_tracking.rb → ad_tracking.rb} +5 -5
  30. data/lib/zernio-sdk/models/create_ad_campaign200_response.rb +231 -0
  31. data/lib/zernio-sdk/models/create_ad_campaign_request.rb +52 -1
  32. data/lib/zernio-sdk/models/create_call_ad_request.rb +10 -1
  33. data/lib/zernio-sdk/models/create_messaging_ad_request.rb +10 -1
  34. data/lib/zernio-sdk/models/create_standalone_ad_request.rb +98 -4
  35. data/lib/zernio-sdk/models/ctwa_ad_request_body.rb +10 -1
  36. data/lib/zernio-sdk/models/get_ad_comments200_response_meta.rb +36 -46
  37. data/lib/zernio-sdk/models/get_ios_fourteen_campaign_limits200_response.rb +164 -0
  38. data/lib/zernio-sdk/models/get_ios_fourteen_campaign_limits200_response_limits.rb +172 -0
  39. data/lib/zernio-sdk/models/hide_ad_comment200_response.rb +225 -0
  40. data/lib/zernio-sdk/models/hide_ad_comment_request.rb +165 -0
  41. data/lib/zernio-sdk/models/list_ads_instagram_accounts200_response.rb +220 -0
  42. data/lib/zernio-sdk/models/list_ads_instagram_accounts200_response_accounts_inner.rb +287 -0
  43. data/lib/zernio-sdk/models/list_ads_instagram_accounts200_response_pages_inner.rb +210 -0
  44. data/lib/zernio-sdk/models/list_ads_instagram_accounts200_response_resolved.rb +211 -0
  45. data/lib/zernio-sdk/models/list_advertisable_applications200_response.rb +166 -0
  46. data/lib/zernio-sdk/models/list_advertisable_applications200_response_applications_inner.rb +250 -0
  47. data/lib/zernio-sdk/models/meta_instagram_identity_ref.rb +202 -0
  48. data/lib/zernio-sdk/models/reply_to_ad_comment200_response.rb +215 -0
  49. data/lib/zernio-sdk/models/reply_to_ad_comment_request.rb +174 -0
  50. data/lib/zernio-sdk/models/targeting_spec.rb +63 -1
  51. data/lib/zernio-sdk/version.rb +1 -1
  52. data/lib/zernio-sdk.rb +15 -2
  53. data/openapi.yaml +618 -143
  54. data/spec/api/ad_accounts_api_spec.rb +91 -2
  55. data/spec/api/ad_campaigns_api_spec.rb +1 -1
  56. data/spec/models/{create_standalone_ad_request_tracking_spec.rb → ad_tracking_spec.rb} +6 -6
  57. data/spec/models/create_ad_campaign200_response_spec.rb +68 -0
  58. data/spec/models/create_ad_campaign_request_spec.rb +28 -0
  59. data/spec/models/create_call_ad_request_spec.rb +6 -0
  60. data/spec/models/create_messaging_ad_request_spec.rb +6 -0
  61. data/spec/models/create_standalone_ad_request_spec.rb +28 -0
  62. data/spec/models/ctwa_ad_request_body_spec.rb +6 -0
  63. data/spec/models/get_ad_comments200_response_meta_spec.rb +19 -1
  64. data/spec/models/get_ios_fourteen_campaign_limits200_response_limits_spec.rb +48 -0
  65. data/spec/models/get_ios_fourteen_campaign_limits200_response_spec.rb +36 -0
  66. data/spec/models/hide_ad_comment200_response_spec.rb +52 -0
  67. data/spec/models/hide_ad_comment_request_spec.rb +36 -0
  68. data/spec/models/list_ads_instagram_accounts200_response_accounts_inner_spec.rb +64 -0
  69. data/spec/models/list_ads_instagram_accounts200_response_pages_inner_spec.rb +54 -0
  70. data/spec/models/list_ads_instagram_accounts200_response_resolved_spec.rb +52 -0
  71. data/spec/models/list_ads_instagram_accounts200_response_spec.rb +48 -0
  72. data/spec/models/list_advertisable_applications200_response_applications_inner_spec.rb +54 -0
  73. data/spec/models/list_advertisable_applications200_response_spec.rb +36 -0
  74. data/spec/models/meta_instagram_identity_ref_spec.rb +48 -0
  75. data/spec/models/reply_to_ad_comment200_response_spec.rb +46 -0
  76. data/spec/models/reply_to_ad_comment_request_spec.rb +36 -0
  77. data/spec/models/targeting_spec_spec.rb +12 -0
  78. data/zernio-sdk-0.0.900.gem +0 -0
  79. metadata +62 -10
  80. data/docs/CreateStandaloneAdRequestPromotedObject.md +0 -38
  81. data/docs/CreateStandaloneAdRequestTracking.md +0 -20
  82. data/lib/zernio-sdk/models/create_standalone_ad_request_promoted_object.rb +0 -249
  83. data/spec/models/create_standalone_ad_request_promoted_object_spec.rb +0 -96
  84. data/zernio-sdk-0.0.899.gem +0 -0
data/openapi.yaml CHANGED
@@ -804,6 +804,27 @@ components:
804
804
  effective_account_limit: 2000
805
805
  current_account_count: 2000
806
806
  schemas:
807
+ AdTracking:
808
+ type: object
809
+ description: "Meta only. Attaches pixel measurement to the ad regardless of the optimization goal (the \"Website events\" tracking row in Ads Manager). `pixelId` becomes the ad's `tracking_specs` (offsite_conversion + fb_pixel); `urlTags` is stored on the new creative as `url_tags` and retained on the ad for compatibility. Applied on the legacy single-creative shape, every ad of the multi-creative shape, and the attach shape. NOTE: tracking lives on the AD object and is not inherited from the ad set, so pass it on EVERY attach call that should carry the pixel."
810
+ properties:
811
+ pixelId: { type: string, description: "Meta Pixel ID to attach for offsite-conversion measurement." }
812
+ urlTags:
813
+ type: array
814
+ description: "Click-URL params stored on the creative as `url_tags` and returned by GET /v1/ads/{adId}/tracking-tags. App-promotion linkUrl stays byte-identical to promotedObject.objectStoreUrl. Meta dynamic macros ({{ad.id}}, {{campaign.id}}, {{placement}}, ...) are sent through unescaped so Meta expands them; every other character is percent-encoded."
815
+ items:
816
+ type: object
817
+ required: [key, value]
818
+ properties:
819
+ key: { type: string }
820
+ value: { type: string }
821
+ MetaInstagramIdentityRef:
822
+ type: object
823
+ required: [igUserId, username]
824
+ properties:
825
+ igUserId: { type: string, description: "Instagram identity ID." }
826
+ username: { type: string, description: "Instagram username; empty when Meta does not expose it." }
827
+ profilePictureUrl: { type: string, description: "Profile picture URL when available." }
807
828
  GoogleBusinessReview:
808
829
  type: object
809
830
  description: A Google Business Profile review, as returned by every gmb-reviews read endpoint.
@@ -889,6 +910,7 @@ components:
889
910
  creativeFeatures:
890
911
  $ref: '#/components/schemas/MetaCreativeFeatures'
891
912
  description: 'Meta enhancement settings for single or attached ads, and defaults for creatives[]. An item replaces the entire map, including with an empty object.'
913
+ tracking: { $ref: '#/components/schemas/AdTracking' }
892
914
  accountId:
893
915
  type: string
894
916
  minLength: 1
@@ -8812,6 +8834,114 @@ components:
8812
8834
  properties:
8813
8835
  amount: { type: number }
8814
8836
  type: { type: string, enum: [daily, lifetime] }
8837
+ AdPromotedObject:
8838
+ type: object
8839
+ description: |
8840
+ What the ad optimises against. Behaviour depends on the platform.
8841
+
8842
+ **Meta**: forwarded to the ad set's `promoted_object` (snake-cased).
8843
+ For `goal: app_promotion`, it is also sent on the campaign only when
8844
+ `isSkadnetworkAttribution: true`. Plain Android app installs keep the
8845
+ existing campaign payload, with the promoted object only on the ad set.
8846
+ POST /v1/ads/campaigns forwards this object only for that explicit SKAN flag.
8847
+ Required for goals whose ad-set optimization_goal points at a specific
8848
+ event/page/app (without it Meta rejects the ad-set create with
8849
+ `error_subcode: 1815430` "Please select a promoted object for your ad set"):
8850
+ - `goal: conversions` / `lead_conversion` (OFFSITE_CONVERSIONS): requires `pixelId` + `customEventType`, or `customConversionId` when optimising against a Custom Conversion (the conversion carries its own event definition). For a pixel CUSTOM event (one you named yourself in CAPI/Events Manager), send `customEventType: OTHER` + `customEventStr` with the event name.
8851
+ - `goal: app_promotion` (APP_INSTALLS): requires `applicationId` + `objectStoreUrl`
8852
+ - `goal: lead_generation` (LEAD_GENERATION): `pageId` is auto-filled from the connected Page when omitted
8853
+
8854
+ Other Meta goals (engagement, traffic, awareness, video_views) ignore this field.
8855
+
8856
+ **TikTok**: used by `goal: conversions` and the Smart+ goals (`smartPlus: true`).
8857
+ - `pixelId` maps to the ad group's `pixel_id`. Required: a TikTok website-conversion
8858
+ ad group without a pixel is rejected with `40002: Please select a pixel`.
8859
+ - `customEventType` maps to the ad group's `optimization_event` (the pixel event to
8860
+ optimise for). Optional on the regular conversions flow, required on Smart+.
8861
+ See the `customEventType` field below for the valid TikTok codes.
8862
+ - `applicationId` (Smart+ `goal: app_promotion` only) maps to the ad group's `app_id`:
8863
+ the App ID of an app registered on the TikTok Ads account (Assets → Events →
8864
+ App Events). Install optimization needs the app's MMP tracking configured.
8865
+
8866
+ The remaining `promotedObject.*` fields are Meta-only. Platforms other than
8867
+ Meta and TikTok ignore `promotedObject` entirely.
8868
+ properties:
8869
+ pixelId:
8870
+ type: string
8871
+ description: |
8872
+ Pixel ID. **Meta:** Facebook Pixel ID, required for `goal: conversions`.
8873
+ Requires `customEventType` alongside it; Meta rejects any promoted_object
8874
+ carrying `pixel_id` without `custom_event_type` (error_subcode 1885014),
8875
+ even when `customConversionId` is also present.
8876
+ **TikTok:** TikTok Pixel ID, required for `goal: conversions`.
8877
+ To discover the pixels an ad account can use, call
8878
+ `GET /v1/accounts/{accountId}/tracking-tags?adAccountId=act_...` (each entry
8879
+ carries `kind` and `ownerAdAccountId`), or
8880
+ `GET /v1/accounts/{accountId}/conversion-destinations`. Note this is a
8881
+ different resource from `GET /v1/ads/{adId}/tracking-tags`, which reads an
8882
+ ad's click-URL params (`url_tags`), not pixels.
8883
+ customEventType:
8884
+ type: string
8885
+ description: |
8886
+ The event the campaign/ad group optimises against.
8887
+
8888
+ **Meta:** standard event like `PURCHASE`, `LEAD`, `COMPLETE_REGISTRATION`,
8889
+ `ADD_TO_CART`. Uppercased internally so callers can pass any case. Required
8890
+ for `goal: conversions`.
8891
+
8892
+ **TikTok:** an `optimization_event` code (UPPER_SNAKE, not Meta's vocabulary
8893
+ and not PascalCase), OR the exact event name shown in TikTok Events Manager
8894
+ (auto-resolved to its code). Must be one of the event types your TikTok
8895
+ Pixel tracks; custom events are not optimizable. Current taxonomy:
8896
+ `SHOPPING` (Purchase), `ON_WEB_CART` (Add to Cart), `INITIATE_ORDER`
8897
+ (Initiate Checkout), `FORM` (Lead), `ON_WEB_REGISTER` (Complete
8898
+ Registration), `ON_WEB_DETAIL` (View Content). `ON_WEB_ORDER` is
8899
+ deprecated. On rejection the error lists the event types your pixel
8900
+ actually tracks. Optional for `goal: conversions`.
8901
+ customEventStr:
8902
+ type: string
8903
+ description: |
8904
+ Meta only. Pixel custom-event name to optimise against (Meta's
8905
+ `custom_event_str`), exactly as it appears in Events Manager and in your
8906
+ CAPI payloads (case-sensitive, not uppercased). Requires
8907
+ `customEventType: OTHER`, and `OTHER` requires this field (400 either way).
8908
+ The same as picking a custom event in Ads Manager's conversion-event
8909
+ dropdown. For rule-based Custom Conversions use `customConversionId`
8910
+ instead.
8911
+ pageId:
8912
+ type: string
8913
+ description: |
8914
+ Facebook Page ID. Used by `goal: lead_generation`. Auto-filled from the
8915
+ connected Page when omitted.
8916
+ applicationId:
8917
+ type: string
8918
+ description: "App ID. Required for `goal: app_promotion`."
8919
+ objectStoreUrl:
8920
+ type: string
8921
+ format: uri
8922
+ description: "App Store / Play Store listing URL. Required for `goal: app_promotion`."
8923
+ customConversionId:
8924
+ type: string
8925
+ description: |
8926
+ Custom Conversion ID, when optimising against one instead of a standard
8927
+ event. Accepted alone by this API, without `pixelId` or `customEventType`.
8928
+ If `pixelId` is also sent, `customEventType` is still required on the
8929
+ promoted_object (Meta rejects `pixel_id` without `custom_event_type`,
8930
+ error_subcode 1885014).
8931
+ productCatalogId:
8932
+ type: string
8933
+ description: "Optional catalog ID. If supplied with productSetId, the set must belong to this catalog. A catalog ID cannot replace productSetId."
8934
+ productSetId:
8935
+ type: string
8936
+ 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."
8937
+ offlineConversionDataSetId:
8938
+ type: string
8939
+ 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).'
8940
+ whatsappPhoneNumber:
8941
+ type: string
8942
+ description: 'Meta only. WhatsApp number on messaging-destination ad sets.'
8943
+ additionalProperties: false
8944
+
8815
8945
  TargetingSpec:
8816
8946
  type: object
8817
8947
  description: |
@@ -8829,6 +8959,16 @@ components:
8829
8959
  platforms. Fields a platform cannot honour are rejected at create time with
8830
8960
  `INVALID_FIELD_VALUE` naming the offending field (not silently dropped).
8831
8961
  properties:
8962
+ userOs:
8963
+ type: array
8964
+ minItems: 1
8965
+ items: { type: string, minLength: 1 }
8966
+ description: 'Meta only. Operating systems and version ranges, such as iOS_ver_14.0_and_above or Android. Emitted as user_os. May also be supplied inside targeting.'
8967
+ userDevice:
8968
+ type: array
8969
+ minItems: 1
8970
+ items: { type: string, minLength: 1 }
8971
+ description: 'Meta only. Device models such as iPhone. Emitted as user_device. May also be supplied inside targeting.'
8832
8972
  countries: { type: array, items: { type: string }, description: "ISO 3166-1 alpha-2 country codes (e.g. ['US'])." }
8833
8973
  regions:
8834
8974
  type: array
@@ -42919,6 +43059,18 @@ paths:
42919
43059
  type: string
42920
43060
  enum: [engagement, traffic, awareness, video_views, lead_generation, lead_conversion, job_applicants, conversions, app_promotion, catalog_sales, page_likes]
42921
43061
  description: Mapped to the ODAX objective (same mapping as POST /v1/ads/create).
43062
+ isSkadnetworkAttribution:
43063
+ type: boolean
43064
+ description: 'Meta app promotion only. Immutable campaign flag. Set true for iOS 14+ SKAdNetwork campaigns and supply promotedObject.applicationId plus promotedObject.objectStoreUrl. The campaign receives promotedObject only when this flag is true. Cannot be changed on an existing campaign.'
43065
+ promotedObject:
43066
+ $ref: '#/components/schemas/AdPromotedObject'
43067
+ buyingType:
43068
+ type: string
43069
+ enum: [AUCTION, RESERVED]
43070
+ description: 'Meta only. SKAdNetwork app promotion requires AUCTION.'
43071
+ validateOnly:
43072
+ type: boolean
43073
+ description: 'Meta only. Runs campaign validation without creating or persisting a campaign; Idempotency-Key storage is bypassed. Returns HTTP 200 with validateOnly true and status VALIDATED.'
42922
43074
  specialAdCategories:
42923
43075
  type: array
42924
43076
  items: { type: string, enum: [HOUSING, EMPLOYMENT, CREDIT, ISSUES_ELECTIONS_POLITICS, FINANCIAL_PRODUCTS_SERVICES, ONLINE_GAMBLING_AND_GAMING] }
@@ -42932,7 +43084,30 @@ paths:
42932
43084
  bidAmount: { type: number, description: "Whole currency units (USD: 5 = $5.00). Required for LOWEST_COST_WITH_BID_CAP and COST_CAP; ignored otherwise. On Meta, validated here but NOT stored: the campaign object has no bid_amount field, only bid_strategy lives on it, and the amount takes effect once an ad set joins this campaign (existingCampaignId on POST /v1/ads/create) and supplies its own bidAmount there. On Google, stored directly on the campaign's bidding strategy." }
42933
43085
  roasAverageFloor: { type: number, description: "Decimal ROAS multiplier (2.0 = 2.0x). Required for LOWEST_COST_WITH_MIN_ROAS." }
42934
43086
  portfolioBidStrategyId: { type: string, pattern: '^\d+$', description: "Google only. Attach an existing portfolio bid strategy (numeric id from GET /v1/ads/bid-strategies) to the new campaign instead of a standard one. Exclusive with bidStrategy." }
43087
+ example:
43088
+ accountId: '69fc524892b3d8e85f893e73'
43089
+ adAccountId: 'act_757082720485182'
43090
+ name: 'iOS app campaign'
43091
+ goal: app_promotion
43092
+ isSkadnetworkAttribution: true
43093
+ promotedObject: { applicationId: '123456789', objectStoreUrl: 'https://apps.apple.com/us/app/id123456789' }
43094
+ buyingType: AUCTION
43095
+ status: PAUSED
43096
+ validateOnly: true
42935
43097
  responses:
43098
+ '200':
43099
+ description: 'Campaign validation passed without creating a campaign.'
43100
+ content:
43101
+ application/json:
43102
+ schema:
43103
+ type: object
43104
+ properties:
43105
+ validateOnly: { type: boolean, description: 'Always true.' }
43106
+ adAccountId: { type: string }
43107
+ campaignId: { type: string, const: '', description: 'Empty because no campaign was created.' }
43108
+ objective: { type: string }
43109
+ status: { type: string, const: VALIDATED }
43110
+ example: { validateOnly: true, adAccountId: 'act_757082720485182', campaignId: '', objective: OUTCOME_APP_PROMOTION, status: VALIDATED }
42936
43111
  '201':
42937
43112
  description: Campaign created
42938
43113
  content:
@@ -45386,7 +45561,7 @@ paths:
45386
45561
  x-resource-group: "engagement"
45387
45562
  operationId: getAdComments
45388
45563
  tags: ["Ad Accounts"]
45389
- x-platforms: ["meta"]
45564
+ x-platforms: ["meta", "tiktok"]
45390
45565
  summary: List comments on an ad
45391
45566
  description: |
45392
45567
  Returns comments on an ad's underlying creative post. Useful for moderating or analyzing
@@ -45406,26 +45581,39 @@ paths:
45406
45581
  Instagram account on the profile can read the ad's media, the call returns
45407
45582
  ads_connection_required (the Facebook side, if any, is still readable via ?placement=facebook).
45408
45583
 
45409
- Meta-only for now. Other ad platforms (TikTok, LinkedIn, Pinterest, Google, X)
45410
- are not wired to this endpoint and return feature_not_available.
45584
+ TikTok uses the connected TikTok Ads advertiser token and supports both paid video
45585
+ ads and Spark Ads. `since` and `until` select a date window of at most 30 days;
45586
+ the default is the last 30 days. TikTok searches by ad group, so Zernio filters
45587
+ each page to this ad. A page can be empty while `pagination.hasMore` is true.
45588
+ Reuse `pagination.cursor` with the same `limit`; the cursor retains the date window.
45589
+ `placement` is Meta-only and returns a 400 for TikTok.
45590
+
45591
+ TikTok returns replies as separate comments with `parentId`; nested reply fetching
45592
+ is not supported. `canReply` requires a first-level comment and an identity with
45593
+ comment-management permission. `canDelete` reflects TikTok's own-comment deletion
45594
+ capability. `canHide` is supported and `canLike` is false. Use the ad comment
45595
+ reply, hide and delete operations below to moderate TikTok comments.
45596
+ Other platforms return feature_not_available.
45411
45597
 
45412
45598
  Requires the Ads add-on. Response shape matches GET /v1/inbox/comments/{postId}.
45413
45599
 
45414
45600
  The `{adId}` path segment accepts any identifier dialect Zernio indexes for the ad:
45415
- Zernio internal `_id` (24-char hex), Meta's numeric `platformAdId` (the value shipped in
45601
+ Zernio internal `_id` (24-char hex), the numeric `platformAdId` (the value shipped in
45416
45602
  `comment.received` webhooks as `comment.ad.id`), or the creative's
45417
45603
  `effective_object_story_id` / `effective_instagram_media_id`. Caller doesn't need a
45418
45604
  translation step.
45419
45605
  security:
45420
45606
  - bearerAuth: []
45421
45607
  parameters:
45422
- - { name: adId, in: path, required: true, schema: { type: string }, description: "Internal Zernio ad ID (ObjectId)." }
45608
+ - { name: adId, in: path, required: true, schema: { type: string }, description: "Internal Zernio ad ID or indexed platform ad/post ID." }
45423
45609
  - { name: placement, in: query, schema: { type: string, enum: [facebook, instagram] }, description: "Which side of the ad to return comments for. Omit to default to the Instagram side when present, else Facebook. Returns ad_not_commentable if the ad has no such placement." }
45424
45610
  - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } }
45611
+ - { name: since, in: query, schema: { type: string, format: date }, description: "TikTok-only start date. Defaults to 30 days before until. Maximum window is 30 days." }
45612
+ - { name: until, in: query, schema: { type: string, format: date }, description: "TikTok-only end date. Defaults to today in UTC." }
45425
45613
  - { name: cursor, in: query, schema: { type: string }, description: "Pagination cursor from a previous response." }
45426
45614
  responses:
45427
45615
  '200':
45428
- description: Comments on the ad
45616
+ description: "Comments on the ad."
45429
45617
  content:
45430
45618
  application/json:
45431
45619
  schema:
@@ -45437,7 +45625,7 @@ paths:
45437
45625
  type: array
45438
45626
  items:
45439
45627
  type: object
45440
- description: Normalized comment. Same shape as /v1/inbox/comments/{postId} responses.
45628
+ description: "Normalized comment. Same shape as /v1/inbox/comments/{postId} responses."
45441
45629
  pagination:
45442
45630
  type: object
45443
45631
  properties:
@@ -45445,15 +45633,20 @@ paths:
45445
45633
  cursor: { type: string }
45446
45634
  meta:
45447
45635
  type: object
45448
- required: [platform, placement, adId, platformAdId, effectiveStoryId, accountId, lastUpdated]
45636
+ required: [platform, adId, accountId, lastUpdated]
45449
45637
  properties:
45450
- platform: { type: string, enum: [facebook, instagram], description: "Which side these comments are on (same as `placement`)." }
45638
+ platform: { type: string, enum: [facebook, instagram, tiktok], description: "Platform of the comments." }
45451
45639
  placement: { type: string, enum: [facebook, instagram], description: "The placement these comments are for, useful when you didn't pass ?placement= and want to know which one you got." }
45452
45640
  adId: { type: string, description: "Internal Zernio ad ID." }
45453
- platformAdId: { type: string, description: "Meta ad ID." }
45641
+ platformAdId: { type: string, description: "Platform ad ID." }
45454
45642
  effectiveStoryId:
45455
45643
  type: string
45456
45644
  description: "Underlying post ID the comments belong to. effective_object_story_id for the Facebook side, effective_instagram_media_id for the Instagram side."
45645
+ tiktokItemId:
45646
+ type: [string, "null"]
45647
+ description: "TikTok-only video item ID. Null when the ad and comments do not expose it."
45648
+ since: { type: string, format: date, description: "TikTok-only resolved start date." }
45649
+ until: { type: string, format: date, description: "TikTok-only resolved end date." }
45457
45650
  facebookAccountId:
45458
45651
  type: [string, "null"]
45459
45652
  description: "Facebook-only. The connected Facebook Page SocialAccount these comments were read through. Pass it as `accountId` (with `effectiveStoryId` as the postId) to /v1/inbox/comments to reply/hide/delete. Null when no connected Page was used (then moderation isn't possible)."
@@ -45468,19 +45661,215 @@ paths:
45468
45661
  description: "Instagram-only. The connected Instagram SocialAccount these comments were read through. Pass it as `accountId` (with `effectiveStoryId` as the postId) to /v1/inbox/comments to reply/hide/delete."
45469
45662
  accountId: { type: string, description: "Account ID (ads SocialAccount)." }
45470
45663
  lastUpdated: { type: string, format: date-time }
45664
+ example:
45665
+ status: success
45666
+ comments:
45667
+ - id: "7512345678901234567"
45668
+ message: "Can you share more details?"
45669
+ createdTime: "2026-09-08T10:00:00.000Z"
45670
+ from: { id: "6123456789123456789", name: "reader", username: "reader", picture: "https://example.com/avatar.jpg", isOwner: false }
45671
+ likeCount: 4
45672
+ replyCount: 0
45673
+ platform: tiktok
45674
+ url: null
45675
+ replies: []
45676
+ isHidden: false
45677
+ canReply: true
45678
+ canDelete: false
45679
+ canHide: true
45680
+ canLike: false
45681
+ isLiked: false
45682
+ pagination: { hasMore: false }
45683
+ meta:
45684
+ platform: tiktok
45685
+ adId: "507f1f77bcf86cd799439011"
45686
+ platformAdId: "1790166588666881"
45687
+ accountId: "507f1f77bcf86cd799439012"
45688
+ tiktokItemId: "7512345678901234500"
45689
+ since: "2026-08-10"
45690
+ until: "2026-09-09"
45691
+ lastUpdated: "2026-09-09T12:00:00.000Z"
45471
45692
  '400':
45472
45693
  description: |
45473
45694
  Invalid ad ID format, or the ad's creative format does not expose a commentable
45474
45695
  underlying post (code ad_not_commentable).
45475
45696
  '401': { $ref: '#/components/responses/Unauthorized' }
45476
45697
  '403':
45477
- description: Ads access required (legacy plans need the Ads add-on; included by default on usage-based plans), or ad platform is not Meta (code feature_not_available).
45698
+ description: Ads access required (legacy plans need the Ads add-on; included by default on usage-based plans), or ad platform is not Meta or TikTok (code feature_not_available).
45478
45699
  '404': { $ref: '#/components/responses/NotFound' }
45479
45700
  '422':
45480
45701
  description: |
45481
45702
  Ads account token unavailable, or (for Instagram-placed ads) no connected
45482
45703
  Instagram account on the profile can read the ad's media (code ads_connection_required).
45483
45704
 
45705
+ /v1/ads/{adId}/comments/{commentId}/reply:
45706
+ post:
45707
+ operationId: replyToAdComment
45708
+ summary: Reply to an ad comment
45709
+ tags: ["Ad Accounts"]
45710
+ x-resource-group: "engagement"
45711
+ x-platforms: ["tiktok"]
45712
+ description: |
45713
+ Reply to a first-level TikTok ad comment. Requires a TT_USER or CUSTOMIZED_USER identity with comment-management permission. Replies to replies are rejected. The response commentId identifies the new reply. This operation is not idempotent; do not blindly retry an uncertain response.
45714
+
45715
+ Requires Ads access. The ad is resolved within the caller's accessible profiles.
45716
+ Before moderation, Zernio verifies that the comment belongs to this ad using
45717
+ TikTok's ad-group comment listing. The default search window is the last 30 days.
45718
+ Use since/until for older comments, with at most 30 days between the dates.
45719
+ Lookups scan at most 2,000 ad-group comments; narrow the date window if exceeded.
45720
+ Meta returns 501 feature_not_available with guidance to use the existing inbox
45721
+ comment endpoints and the account/post IDs from GET /v1/ads/{adId}/comments.
45722
+ security:
45723
+ - bearerAuth: []
45724
+ parameters:
45725
+ - { name: adId, in: path, required: true, schema: { type: string }, description: "Internal Zernio ad ID or indexed platform ad ID." }
45726
+ - { name: commentId, in: path, required: true, schema: { type: string, pattern: '^\d+$' }, description: "TikTok comment ID from the ad comment listing." }
45727
+ - { name: since, in: query, schema: { type: string, format: date }, description: "Start date of the comment lookup window. Defaults to 30 days before until." }
45728
+ - { name: until, in: query, schema: { type: string, format: date }, description: "End date of the comment lookup window. Defaults to today in UTC." }
45729
+ requestBody:
45730
+ required: true
45731
+ content:
45732
+ application/json:
45733
+ schema:
45734
+ type: object
45735
+ required: [text]
45736
+ properties:
45737
+ text: { type: string, minLength: 1, description: "Non-empty reply text." }
45738
+ example: { text: "Thanks for your question!" }
45739
+ responses:
45740
+ '200':
45741
+ description: "Comment action completed."
45742
+ content:
45743
+ application/json:
45744
+ schema:
45745
+ type: object
45746
+ required: [status, commentId]
45747
+ properties:
45748
+ status: { type: string, enum: [success] }
45749
+ commentId: { type: string, description: "ID of the created reply or moderated comment." }
45750
+ example: { status: success, commentId: "7512345678901234567" }
45751
+ '400': { $ref: '#/components/responses/BadRequest' }
45752
+ '401': { $ref: '#/components/responses/Unauthorized' }
45753
+ '403':
45754
+ description: "Ads access or the required TikTok comment capability is unavailable."
45755
+ '404':
45756
+ description: "Ad is inaccessible or the comment was not found on this ad in the selected date window."
45757
+ '422':
45758
+ description: "TikTok Ads connection is unavailable."
45759
+ '501':
45760
+ description: "Moderation on this route supports TikTok. Use the inbox comment routes for Meta."
45761
+ '502':
45762
+ description: "TikTok rejected the request or was unavailable. Inspect platformError for its code and message."
45763
+
45764
+ /v1/ads/{adId}/comments/{commentId}/hide:
45765
+ post:
45766
+ operationId: hideAdComment
45767
+ summary: Hide or unhide an ad comment
45768
+ tags: ["Ad Accounts"]
45769
+ x-resource-group: "engagement"
45770
+ x-platforms: ["tiktok"]
45771
+ description: |
45772
+ Hide or restore a TikTok ad comment. Send hidden=true to hide it or hidden=false to make it public again.
45773
+
45774
+ Requires Ads access. The ad is resolved within the caller's accessible profiles.
45775
+ Before moderation, Zernio verifies that the comment belongs to this ad using
45776
+ TikTok's ad-group comment listing. The default search window is the last 30 days.
45777
+ Use since/until for older comments, with at most 30 days between the dates.
45778
+ Lookups scan at most 2,000 ad-group comments; narrow the date window if exceeded.
45779
+ Meta returns 501 feature_not_available with guidance to use the existing inbox
45780
+ comment endpoints and the account/post IDs from GET /v1/ads/{adId}/comments.
45781
+ security:
45782
+ - bearerAuth: []
45783
+ parameters:
45784
+ - { name: adId, in: path, required: true, schema: { type: string }, description: "Internal Zernio ad ID or indexed platform ad ID." }
45785
+ - { name: commentId, in: path, required: true, schema: { type: string, pattern: '^\d+$' }, description: "TikTok comment ID from the ad comment listing." }
45786
+ - { name: since, in: query, schema: { type: string, format: date }, description: "Start date of the comment lookup window. Defaults to 30 days before until." }
45787
+ - { name: until, in: query, schema: { type: string, format: date }, description: "End date of the comment lookup window. Defaults to today in UTC." }
45788
+ requestBody:
45789
+ required: true
45790
+ content:
45791
+ application/json:
45792
+ schema:
45793
+ type: object
45794
+ required: [hidden]
45795
+ properties:
45796
+ hidden: { type: boolean, description: "True to hide the comment; false to restore it." }
45797
+ example: { hidden: true }
45798
+ responses:
45799
+ '200':
45800
+ description: "Comment action completed."
45801
+ content:
45802
+ application/json:
45803
+ schema:
45804
+ type: object
45805
+ required: [status, commentId]
45806
+ properties:
45807
+ status: { type: string, enum: [success] }
45808
+ commentId: { type: string, description: "ID of the created reply or moderated comment." }
45809
+ hidden: { type: boolean, description: "The requested visibility state." }
45810
+ example: { status: success, commentId: "7512345678901234567", hidden: true }
45811
+ '400': { $ref: '#/components/responses/BadRequest' }
45812
+ '401': { $ref: '#/components/responses/Unauthorized' }
45813
+ '403':
45814
+ description: "Ads access or the required TikTok comment capability is unavailable."
45815
+ '404':
45816
+ description: "Ad is inaccessible or the comment was not found on this ad in the selected date window."
45817
+ '422':
45818
+ description: "TikTok Ads connection is unavailable."
45819
+ '501':
45820
+ description: "Moderation on this route supports TikTok. Use the inbox comment routes for Meta."
45821
+ '502':
45822
+ description: "TikTok rejected the request or was unavailable. Inspect platformError for its code and message."
45823
+
45824
+ /v1/ads/{adId}/comments/{commentId}:
45825
+ delete:
45826
+ operationId: deleteAdComment
45827
+ summary: Delete an ad comment
45828
+ tags: ["Ad Accounts"]
45829
+ x-resource-group: "engagement"
45830
+ x-platforms: ["tiktok"]
45831
+ description: |
45832
+ Delete your own TikTok ad comment or reply. TikTok must return can_delete=true for the comment. Other users' comments can be hidden instead.
45833
+
45834
+ Requires Ads access. The ad is resolved within the caller's accessible profiles.
45835
+ Before moderation, Zernio verifies that the comment belongs to this ad using
45836
+ TikTok's ad-group comment listing. The default search window is the last 30 days.
45837
+ Use since/until for older comments, with at most 30 days between the dates.
45838
+ Lookups scan at most 2,000 ad-group comments; narrow the date window if exceeded.
45839
+ Meta returns 501 feature_not_available with guidance to use the existing inbox
45840
+ comment endpoints and the account/post IDs from GET /v1/ads/{adId}/comments.
45841
+ security:
45842
+ - bearerAuth: []
45843
+ parameters:
45844
+ - { name: adId, in: path, required: true, schema: { type: string }, description: "Internal Zernio ad ID or indexed platform ad ID." }
45845
+ - { name: commentId, in: path, required: true, schema: { type: string, pattern: '^\d+$' }, description: "TikTok comment ID from the ad comment listing." }
45846
+ - { name: since, in: query, schema: { type: string, format: date }, description: "Start date of the comment lookup window. Defaults to 30 days before until." }
45847
+ - { name: until, in: query, schema: { type: string, format: date }, description: "End date of the comment lookup window. Defaults to today in UTC." }
45848
+ responses:
45849
+ '200':
45850
+ description: "Comment action completed."
45851
+ content:
45852
+ application/json:
45853
+ schema:
45854
+ type: object
45855
+ required: [status, commentId]
45856
+ properties:
45857
+ status: { type: string, enum: [success] }
45858
+ commentId: { type: string, description: "ID of the created reply or moderated comment." }
45859
+ example: { status: success, commentId: "7512345678901234567" }
45860
+ '400': { $ref: '#/components/responses/BadRequest' }
45861
+ '401': { $ref: '#/components/responses/Unauthorized' }
45862
+ '403':
45863
+ description: "Ads access or the required TikTok comment capability is unavailable."
45864
+ '404':
45865
+ description: "Ad is inaccessible or the comment was not found on this ad in the selected date window."
45866
+ '422':
45867
+ description: "TikTok Ads connection is unavailable."
45868
+ '501':
45869
+ description: "Moderation on this route supports TikTok. Use the inbox comment routes for Meta."
45870
+ '502':
45871
+ description: "TikTok rejected the request or was unavailable. Inspect platformError for its code and message."
45872
+
45484
45873
  /v1/ads/business-centers:
45485
45874
  get:
45486
45875
  x-resource-group: "ads"
@@ -45736,6 +46125,159 @@ paths:
45736
46125
  '401': { $ref: '#/components/responses/Unauthorized' }
45737
46126
  '501': { description: Only supported on Meta (facebook/instagram) }
45738
46127
 
46128
+ /v1/ads/instagram-accounts:
46129
+ get:
46130
+ operationId: listAdsInstagramAccounts
46131
+ summary: List Instagram ad identities
46132
+ description: "Discovers identities through connected_instagram_accounts, Page linkage and Page-backed identities, with a best-effort business fallback. Business permission errors do not fail discovery. The resolved object uses the same profile-scoped resolver as ad creation; null means no identity was resolved. Format-specific observed-actor fallbacks at creative creation are not predicted."
46133
+ tags: ["Ad Accounts"]
46134
+ x-resource-group: "ads"
46135
+ x-platforms: ["meta"]
46136
+ security:
46137
+ - bearerAuth: []
46138
+ parameters:
46139
+ - { name: accountId, in: query, required: true, schema: { type: string }, description: "Zernio Meta Ads or Facebook SocialAccount ID." }
46140
+ - { name: adAccountId, in: query, required: true, schema: { type: string, pattern: '^act_[0-9]+$' }, description: "Meta ad account ID including the act_ prefix." }
46141
+ responses:
46142
+ '200':
46143
+ description: "Instagram identities and Page linkage."
46144
+ content:
46145
+ application/json:
46146
+ schema:
46147
+ type: object
46148
+ required: [accounts, pages, resolved]
46149
+ properties:
46150
+ accounts:
46151
+ type: array
46152
+ items:
46153
+ allOf:
46154
+ - $ref: '#/components/schemas/MetaInstagramIdentityRef'
46155
+ - type: object
46156
+ required: [isPageBacked, source]
46157
+ properties:
46158
+ isPageBacked: { type: boolean, description: "Whether this is a Page-backed Instagram identity." }
46159
+ source: { type: string, enum: [ad_account, page_backed, business], description: "Discovery source; Page linkage also uses page_backed." }
46160
+ pages:
46161
+ type: array
46162
+ items:
46163
+ type: object
46164
+ required: [pageId, name]
46165
+ properties:
46166
+ pageId: { type: string, description: "Facebook Page ID." }
46167
+ name: { type: string, description: "Facebook Page name." }
46168
+ instagramBusinessAccount: { $ref: '#/components/schemas/MetaInstagramIdentityRef' }
46169
+ connectedInstagramAccount: { $ref: '#/components/schemas/MetaInstagramIdentityRef' }
46170
+ resolved:
46171
+ type: object
46172
+ required: [pageId, igUserId, source]
46173
+ properties:
46174
+ pageId: { type: [string, "null"], description: "Page selected by the shared ad-creation resolver." }
46175
+ igUserId: { type: [string, "null"], description: "Instagram identity selected by the shared ad-creation resolver." }
46176
+ source: { type: [string, "null"], enum: [ad_account, page_backed, business, null], description: "Discovery source of the resolved identity; null when absent from discovery." }
46177
+ example:
46178
+ accounts:
46179
+ - { igUserId: "17841400000000000", username: "example", isPageBacked: false, source: page_backed }
46180
+ pages:
46181
+ - pageId: "123456789"
46182
+ name: "Example Page"
46183
+ instagramBusinessAccount: { igUserId: "17841400000000000", username: "example" }
46184
+ resolved: { pageId: "123456789", igUserId: "17841400000000000", source: page_backed }
46185
+ '400': { $ref: '#/components/responses/BadRequest' }
46186
+ '401': { $ref: '#/components/responses/Unauthorized' }
46187
+ '403': { description: "The account or Meta asset is not accessible." }
46188
+ '404': { $ref: '#/components/responses/NotFound' }
46189
+ '501': { description: "Only supported on Meta Ads and Facebook accounts." }
46190
+
46191
+ /v1/ads/advertisable-applications:
46192
+ get:
46193
+ operationId: listAdvertisableApplications
46194
+ summary: List advertisable apps
46195
+ description: "Lists applications available to a Meta ad account, their supported platforms and unmodified object store URLs. A listed app still needs a configured mobile platform and store URL to run install promotion."
46196
+ tags: ["Ad Accounts"]
46197
+ x-resource-group: "ads"
46198
+ x-platforms: ["meta"]
46199
+ security:
46200
+ - bearerAuth: []
46201
+ parameters:
46202
+ - { name: accountId, in: query, required: true, schema: { type: string }, description: "Zernio Meta Ads or Facebook SocialAccount ID." }
46203
+ - { name: adAccountId, in: query, required: true, schema: { type: string, pattern: '^act_[0-9]+$' }, description: "Meta ad account ID including the act_ prefix." }
46204
+ responses:
46205
+ '200':
46206
+ description: "Applications available for promotion."
46207
+ content:
46208
+ application/json:
46209
+ schema:
46210
+ type: object
46211
+ required: [applications]
46212
+ properties:
46213
+ applications:
46214
+ type: array
46215
+ items:
46216
+ type: object
46217
+ required: [id, name, supportedPlatforms, storeUrls]
46218
+ properties:
46219
+ id: { type: string, description: "Meta application ID." }
46220
+ name: { type: string, description: "Application name." }
46221
+ supportedPlatforms:
46222
+ type: array
46223
+ items: { type: string }
46224
+ description: "Platform identifiers reported by Meta."
46225
+ storeUrls:
46226
+ type: object
46227
+ additionalProperties: { type: string }
46228
+ description: "Platform-keyed store URLs returned unchanged by Meta."
46229
+ example:
46230
+ applications:
46231
+ - id: "123456789"
46232
+ name: "Example App"
46233
+ supportedPlatforms: [IOS, ANDROID]
46234
+ storeUrls: { iphone: "https://apps.apple.com/app/id123456789", google_play: "https://play.google.com/store/apps/details?id=com.example.app" }
46235
+ '400': { $ref: '#/components/responses/BadRequest' }
46236
+ '401': { $ref: '#/components/responses/Unauthorized' }
46237
+ '403': { description: "The account or Meta asset is not accessible." }
46238
+ '404': { $ref: '#/components/responses/NotFound' }
46239
+ '501': { description: "Only supported on Meta Ads and Facebook accounts." }
46240
+
46241
+ /v1/ads/ios-fourteen-campaign-limits:
46242
+ get:
46243
+ operationId: getIosFourteenCampaignLimits
46244
+ summary: Get iOS 14 campaign limits
46245
+ description: "Reads Meta iOS 14 campaign limits for an application on an ad account. applicationId is sent as Meta app_id. This read does not establish that the application is configured for iOS promotion."
46246
+ tags: ["Ad Accounts"]
46247
+ x-resource-group: "ads"
46248
+ x-platforms: ["meta"]
46249
+ security:
46250
+ - bearerAuth: []
46251
+ parameters:
46252
+ - { name: accountId, in: query, required: true, schema: { type: string }, description: "Zernio Meta Ads or Facebook SocialAccount ID." }
46253
+ - { name: adAccountId, in: query, required: true, schema: { type: string, pattern: '^act_[0-9]+$' }, description: "Meta ad account ID including the act_ prefix." }
46254
+ - { name: applicationId, in: query, required: true, schema: { type: string, pattern: '^[0-9]+$' }, description: "Meta application ID from advertisable-applications." }
46255
+ responses:
46256
+ '200':
46257
+ description: "Application campaign limits."
46258
+ content:
46259
+ application/json:
46260
+ schema:
46261
+ type: object
46262
+ required: [limits]
46263
+ properties:
46264
+ limits:
46265
+ type: [object, "null"]
46266
+ properties:
46267
+ campaignGroupLimit: { type: [number, "null"], description: "Campaign group limit reported by Meta." }
46268
+ campaignLimit: { type: [number, "null"], description: "Campaign limit reported by Meta." }
46269
+ campaignGroupLimitsDetails:
46270
+ type: array
46271
+ items: {}
46272
+ description: "Campaign group limit details returned by Meta."
46273
+ example:
46274
+ limits: { campaignGroupLimit: 9, campaignLimit: 5, campaignGroupLimitsDetails: [] }
46275
+ '400': { $ref: '#/components/responses/BadRequest' }
46276
+ '401': { $ref: '#/components/responses/Unauthorized' }
46277
+ '403': { description: "The account or Meta asset is not accessible." }
46278
+ '404': { $ref: '#/components/responses/NotFound' }
46279
+ '501': { description: "Only supported on Meta Ads and Facebook accounts." }
46280
+
45739
46281
  /v1/ads/businesses:
45740
46282
  get:
45741
46283
  x-resource-group: "ads"
@@ -47922,20 +48464,7 @@ paths:
47922
48464
  campaignName: { type: string, maxLength: 255, description: "Meta only. Exact campaign name. Overrides the default `<name> - Campaign`." }
47923
48465
  adSetName: { type: string, maxLength: 255, description: "Meta only. Exact ad set name. Overrides the default `<name> - Ad Set`. (For per-ad names on the multi-creative shape, set `name` on each `creatives[]` entry.)" }
47924
48466
  adName: { type: string, maxLength: 255, description: "Meta only. Exact ad name (the single-creative ad object's name). Overrides the default, which is `name`. (For per-ad names on the multi-creative shape, set `name` on each `creatives[]` entry instead.)" }
47925
- tracking:
47926
- type: object
47927
- description: "Meta only. Attaches pixel measurement to the ad regardless of the optimization goal (the \"Website events\" tracking row in Ads Manager). `pixelId` becomes the ad's `tracking_specs` (offsite_conversion + fb_pixel); `urlTags` becomes the ad's `url_tags` (click-tracking query params). Applied on the legacy single-creative shape, every ad of the multi-creative shape, and the attach shape. NOTE: tracking lives on the AD object and is not inherited from the ad set, so pass it on EVERY attach call that should carry the pixel."
47928
- properties:
47929
- pixelId: { type: string, description: "Meta Pixel ID to attach for offsite-conversion measurement." }
47930
- urlTags:
47931
- type: array
47932
- description: "Click-URL params appended to the ad's destination as `url_tags` (e.g. utm_source). Meta dynamic macros ({{ad.id}}, {{campaign.id}}, {{placement}}, ...) are sent through unescaped so Meta expands them; every other character is percent-encoded."
47933
- items:
47934
- type: object
47935
- required: [key, value]
47936
- properties:
47937
- key: { type: string }
47938
- value: { type: string }
48467
+ tracking: { $ref: '#/components/schemas/AdTracking' }
47939
48468
  goal:
47940
48469
  type: string
47941
48470
  enum: [engagement, traffic, awareness, video_views, lead_generation, lead_conversion, conversions, app_promotion, catalog_sales, page_likes, job_applicants]
@@ -47975,7 +48504,7 @@ paths:
47975
48504
  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."
47976
48505
  validateOnly:
47977
48506
  type: boolean
47978
- description: 'Meta only, single standalone shape only (no creatives[], adSetId, or RESERVED). Dry-run: each node runs Meta''s execution_options validate_only and NOTHING is created or persisted. Children need real parents, so a fresh tree validates the campaign + creative (the ad set needs its campaign to exist, so pass existingCampaignId to validate it too; the ad itself is never validatable pre-create). A Meta validation failure returns the 400 verbatim; success returns 200 with per-node results instead of an ad.'
48507
+ description: 'Meta only. Validates the complete inline campaign, ad set, creative and ad with execution_options validate_only. Nothing is uploaded or created, and validation bypasses Idempotency-Key storage. Supports a single image, existing video.id or existingCreativeId; media pools, new video uploads, creatives[], adSetId and RESERVED buying return 400. Existing campaign or creative nodes are marked skipped. Success returns 200 with per-node results; Meta rejection returns an error.'
47979
48508
  budgetAmount: { type: number, description: "Budget in WHOLE currency units (USD: 50 = $50.00), NOT cents. Meta's own Marketing API takes this same number in minor units, so it is an easy and expensive mix-up. Required on legacy + multi-creative shapes. Inherited on attach. OpenAI Ads requires a $1 minimum (its budget is lifetime-only, see budgetType)." }
47980
48509
  budgetType: { type: string, enum: [daily, lifetime], description: "Required on legacy + multi-creative shapes. Inherited on attach. OpenAI Ads accepts lifetime only (no daily-budget concept on the platform); sending daily returns 422. OpenAI Ads lifetime budgets require `endDate` to give the lifetime cap a spend window." }
47981
48510
  status:
@@ -48852,124 +49381,70 @@ paths:
48852
49381
  omitted); TikTok automates delivery within it.
48853
49382
  The budget lives on the Smart+ campaign (Campaign Budget Optimization); a `lifetime`
48854
49383
  budget additionally requires `endDate`. Cannot be combined with `adSetId`.
49384
+ userOs:
49385
+ type: array
49386
+ minItems: 1
49387
+ items: { type: string, minLength: 1 }
49388
+ description: 'Meta only. Operating systems and version ranges, such as iOS_ver_14.0_and_above or Android. Emitted as user_os. May also be supplied inside targeting.'
49389
+ userDevice:
49390
+ type: array
49391
+ minItems: 1
49392
+ items: { type: string, minLength: 1 }
49393
+ description: 'Meta only. Device models such as iPhone. Emitted as user_device. May also be supplied inside targeting.'
49394
+ isSkadnetworkAttribution:
49395
+ type: boolean
49396
+ description: 'Meta app promotion only. Immutable campaign flag. Set true for iOS 14+ SKAdNetwork campaigns and supply promotedObject.applicationId plus promotedObject.objectStoreUrl. The campaign receives promotedObject only when this flag is true. Cannot be changed on an existing campaign.'
49397
+ campaignAttribution:
49398
+ type: string
49399
+ enum: [AEM, SKADNETWORK]
49400
+ description: 'Meta ad-set attribution. Required as SKADNETWORK for iOS 14+ app promotion or a SKAdNetwork campaign. Requires AUCTION buying. Standalone Meta ad-set creation is not supported; use this field on /v1/ads/create.'
48855
49401
  promotedObject:
48856
- type: object
48857
- description: |
48858
- What the ad optimises against. Behaviour depends on the platform.
48859
-
48860
- **Meta**: forwarded to the ad set's `promoted_object` (snake-cased).
48861
- Required for goals whose ad-set optimization_goal points at a specific
48862
- event/page/app (without it Meta rejects the ad-set create with
48863
- `error_subcode: 1815430` "Please select a promoted object for your ad set"):
48864
- - `goal: conversions` / `lead_conversion` (OFFSITE_CONVERSIONS): requires `pixelId` + `customEventType`, or `customConversionId` when optimising against a Custom Conversion (the conversion carries its own event definition). For a pixel CUSTOM event (one you named yourself in CAPI/Events Manager), send `customEventType: OTHER` + `customEventStr` with the event name.
48865
- - `goal: app_promotion` (APP_INSTALLS): requires `applicationId` + `objectStoreUrl`
48866
- - `goal: lead_generation` (LEAD_GENERATION): `pageId` is auto-filled from the connected Page when omitted
48867
-
48868
- Other Meta goals (engagement, traffic, awareness, video_views) ignore this field.
48869
-
48870
- **TikTok**: used by `goal: conversions` and the Smart+ goals (`smartPlus: true`).
48871
- - `pixelId` maps to the ad group's `pixel_id`. Required: a TikTok website-conversion
48872
- ad group without a pixel is rejected with `40002: Please select a pixel`.
48873
- - `customEventType` maps to the ad group's `optimization_event` (the pixel event to
48874
- optimise for). Optional on the regular conversions flow, required on Smart+.
48875
- See the `customEventType` field below for the valid TikTok codes.
48876
- - `applicationId` (Smart+ `goal: app_promotion` only) maps to the ad group's `app_id`:
48877
- the App ID of an app registered on the TikTok Ads account (Assets → Events →
48878
- App Events). Install optimization needs the app's MMP tracking configured.
48879
-
48880
- The remaining `promotedObject.*` fields are Meta-only. Platforms other than
48881
- Meta and TikTok ignore `promotedObject` entirely.
48882
- properties:
48883
- pixelId:
48884
- type: string
48885
- description: |
48886
- Pixel ID. **Meta:** Facebook Pixel ID, required for `goal: conversions`.
48887
- Requires `customEventType` alongside it; Meta rejects any promoted_object
48888
- carrying `pixel_id` without `custom_event_type` (error_subcode 1885014),
48889
- even when `customConversionId` is also present.
48890
- **TikTok:** TikTok Pixel ID, required for `goal: conversions`.
48891
- To discover the pixels an ad account can use, call
48892
- `GET /v1/accounts/{accountId}/tracking-tags?adAccountId=act_...` (each entry
48893
- carries `kind` and `ownerAdAccountId`), or
48894
- `GET /v1/accounts/{accountId}/conversion-destinations`. Note this is a
48895
- different resource from `GET /v1/ads/{adId}/tracking-tags`, which reads an
48896
- ad's click-URL params (`url_tags`), not pixels.
48897
- customEventType:
48898
- type: string
48899
- description: |
48900
- The event the campaign/ad group optimises against.
48901
-
48902
- **Meta:** standard event like `PURCHASE`, `LEAD`, `COMPLETE_REGISTRATION`,
48903
- `ADD_TO_CART`. Uppercased internally so callers can pass any case. Required
48904
- for `goal: conversions`.
48905
-
48906
- **TikTok:** an `optimization_event` code (UPPER_SNAKE, not Meta's vocabulary
48907
- and not PascalCase), OR the exact event name shown in TikTok Events Manager
48908
- (auto-resolved to its code). Must be one of the event types your TikTok
48909
- Pixel tracks; custom events are not optimizable. Current taxonomy:
48910
- `SHOPPING` (Purchase), `ON_WEB_CART` (Add to Cart), `INITIATE_ORDER`
48911
- (Initiate Checkout), `FORM` (Lead), `ON_WEB_REGISTER` (Complete
48912
- Registration), `ON_WEB_DETAIL` (View Content). `ON_WEB_ORDER` is
48913
- deprecated. On rejection the error lists the event types your pixel
48914
- actually tracks. Optional for `goal: conversions`.
48915
- customEventStr:
48916
- type: string
48917
- description: |
48918
- Meta only. Pixel custom-event name to optimise against (Meta's
48919
- `custom_event_str`), exactly as it appears in Events Manager and in your
48920
- CAPI payloads (case-sensitive, not uppercased). Requires
48921
- `customEventType: OTHER`, and `OTHER` requires this field (400 either way).
48922
- The same as picking a custom event in Ads Manager's conversion-event
48923
- dropdown. For rule-based Custom Conversions use `customConversionId`
48924
- instead.
48925
- pageId:
48926
- type: string
48927
- description: |
48928
- Facebook Page ID. Used by `goal: lead_generation`. Auto-filled from the
48929
- connected Page when omitted.
48930
- applicationId:
48931
- type: string
48932
- description: "App ID. Required for `goal: app_promotion`."
48933
- objectStoreUrl:
48934
- type: string
48935
- format: uri
48936
- description: "App Store / Play Store listing URL. Required for `goal: app_promotion`."
48937
- customConversionId:
48938
- type: string
48939
- description: |
48940
- Custom Conversion ID, when optimising against one instead of a standard
48941
- event. Accepted alone by this API, without `pixelId` or `customEventType`.
48942
- If `pixelId` is also sent, `customEventType` is still required on the
48943
- promoted_object (Meta rejects `pixel_id` without `custom_event_type`,
48944
- error_subcode 1885014).
48945
- productCatalogId:
48946
- type: string
48947
- description: "Optional catalog ID. If supplied with productSetId, the set must belong to this catalog. A catalog ID cannot replace productSetId."
48948
- productSetId:
48949
- type: string
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."
48951
- offlineConversionDataSetId:
48952
- type: string
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).'
48954
- whatsappPhoneNumber:
48955
- type: string
48956
- description: 'Meta only. WhatsApp number on messaging-destination ad sets.'
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 }
49402
+ $ref: '#/components/schemas/AdPromotedObject'
49403
+ examples:
49404
+ appPromotion:
49405
+ value:
49406
+ accountId: '69fc524892b3d8e85f893e73'
49407
+ adAccountId: 'act_757082720485182'
49408
+ name: 'iOS app installs'
49409
+ goal: app_promotion
49410
+ isSkadnetworkAttribution: true
49411
+ campaignAttribution: SKADNETWORK
49412
+ buyingType: AUCTION
49413
+ billingEvent: IMPRESSIONS
49414
+ optimizationGoal: APP_INSTALLS
49415
+ promotedObject:
49416
+ applicationId: '123456789'
49417
+ objectStoreUrl: 'https://apps.apple.com/us/app/id123456789'
49418
+ linkUrl: 'https://apps.apple.com/us/app/id123456789'
49419
+ headline: 'Install our app'
49420
+ body: 'Get started today.'
49421
+ callToAction: INSTALL_MOBILE_APP
49422
+ imageUrl: 'https://example.com/app.jpg'
49423
+ targeting:
49424
+ countries: [US]
49425
+ userOs: [iOS_ver_14.0_and_above]
49426
+ tracking:
49427
+ urlTags: [{ key: utm_content, value: '{{ad.id}}' }]
49428
+ budgetAmount: 1
49429
+ budgetType: daily
49430
+ status: PAUSED
49431
+ validateOnly: true
49432
+ retailPromotion:
49433
+ value:
49434
+ accountId: '69fc524892b3d8e85f893e73'
49435
+ adAccountId: act_123456789
49436
+ name: Autumn promotion
49437
+ goal: traffic
49438
+ budgetAmount: 5
49439
+ budgetType: daily
49440
+ status: PAUSED
49441
+ headline: Save on your next order
49442
+ body: Use SAVE20 at checkout.
49443
+ callToAction: SHOP_NOW
49444
+ linkUrl: https://example.com/shop
49445
+ imageUrl: https://example.com/ad.jpg
49446
+ promotion: { type: PERCENTAGE_OFF, value: 20, code: SAVE20 }
49447
+ creativeFeatures: { auto_promotion_tag: OPT_OUT }
48973
49448
  responses:
48974
49449
  '200':
48975
49450
  description: 'validateOnly dry-run passed, nothing was created'