late-sdk 0.0.933 → 0.0.935
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/docs/AdCampaignsApi.md +4 -4
- data/docs/ConnectApi.md +3 -3
- data/docs/CreatePostRequest.md +1 -1
- data/docs/CreateStandaloneAd200ResponseResultsInner.md +1 -1
- data/docs/CreateStandaloneAdRequest.md +2 -2
- data/docs/GetCampaignTargeting200ResponseDevicesInner.md +1 -1
- data/docs/TikTokPlatformData.md +1 -1
- data/docs/UpdateAdRequestTargeting.md +1 -1
- data/docs/UpdateAdRequestTargetingDevicesInnerOneOf.md +1 -1
- data/lib/zernio-sdk/api/ad_campaigns_api.rb +8 -8
- data/lib/zernio-sdk/api/connect_api.rb +4 -4
- data/lib/zernio-sdk/models/create_post_request.rb +1 -1
- data/lib/zernio-sdk/models/create_standalone_ad200_response_results_inner.rb +1 -1
- data/lib/zernio-sdk/models/create_standalone_ad_request.rb +2 -2
- data/lib/zernio-sdk/models/get_campaign_targeting200_response_devices_inner.rb +1 -1
- data/lib/zernio-sdk/models/tik_tok_platform_data.rb +1 -1
- data/lib/zernio-sdk/models/update_ad_request_targeting.rb +1 -1
- data/lib/zernio-sdk/models/update_ad_request_targeting_devices_inner_one_of.rb +6 -6
- data/lib/zernio-sdk/version.rb +1 -1
- data/openapi.yaml +47 -16
- data/spec/api/ad_campaigns_api_spec.rb +4 -4
- data/spec/api/connect_api_spec.rb +2 -2
- data/zernio-sdk-0.0.935.gem +0 -0
- metadata +2 -2
- data/zernio-sdk-0.0.933.gem +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ec3a33a646f8c35273ec8fe41fa081c82333af2508c8ce3e4cfb698c6ba75bd
|
|
4
|
+
data.tar.gz: b1c0841adb72296f86e1d29f776e1e8cef9f4dfe0cfc19c81fa843ce50180db7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 65d214d985af250cfbc269e186a53637d518e46396ba2140f821fa0cf1d1f9efbd81e597a5647cd9b9549d60904d7e6e0446eabfc1e53eec52bf0bd66aa8e148
|
|
7
|
+
data.tar.gz: 7b16eb5991460800c224c12cafb54aee8c79dac920e5fd77784762e48c775187ed624d4d7c61f896d29ebe1f6e741c2f167edf52a9c8ef534b96cb0311dc1d0e
|
data/docs/AdCampaignsApi.md
CHANGED
|
@@ -1537,7 +1537,7 @@ end
|
|
|
1537
1537
|
|
|
1538
1538
|
Read a Google campaign's device, location, and language targeting
|
|
1539
1539
|
|
|
1540
|
-
Google Ads compliance requires geo, language, budget, and bidding targeting set at creation to stay editable afterwards; this reads the campaign state so an integrator can build an editor around it. Cached for the quota window (10 minutes fresh, up to 7 days last-good), not always a live read. Google only; every other platform returns 501. `devices`
|
|
1540
|
+
Google Ads compliance requires geo, language, budget, and bidding targeting set at creation to stay editable afterwards; this reads the campaign state so an integrator can build an editor around it. Cached for the quota window (10 minutes fresh, up to 7 days last-good), not always a live read. Google only; every other platform returns 501. `devices` lists the device criteria the campaign carries, which depends on its channel: Search campaigns have MOBILE, DESKTOP and TABLET, Display campaigns also have CONNECTED_TV. `bidModifier` is Google's bid adjustment for that device, `null` when it has none, and `0` when the device is switched off; `included` is false for exactly that case.
|
|
1541
1541
|
|
|
1542
1542
|
### Examples
|
|
1543
1543
|
|
|
@@ -2769,7 +2769,7 @@ end
|
|
|
2769
2769
|
|
|
2770
2770
|
Update ad
|
|
2771
2771
|
|
|
2772
|
-
Patch one or more fields on an ad. Status, budget, targeting, and creative changes are propagated to the platform. Per-platform support: - **Meta** (Facebook + Instagram): all fields supported. - **TikTok**: status, budget, targeting (via `/v2/adgroup/update/`), and creative (via `/v2/ad/update/` patch-style: `headline` is ignored, `body` becomes `ad_text`). - **Google**: status, budget, KEYWORD edits via `targeting.keywords` / `targeting.negativeKeywords`, DEVICE bid adjustments via `targeting.devices`, LOCATION edits via `targeting.locations` (or the equivalent top-level `targeting.countries` / `regions` / `cities` / `zips` / `metros`), and LANGUAGE edits via `targeting.languages`. Each list you send becomes the FULL new set of its kind (criteria not in the list are removed); a kind left out is untouched.
|
|
2772
|
+
Patch one or more fields on an ad. Status, budget, targeting, and creative changes are propagated to the platform. Per-platform support: - **Meta** (Facebook + Instagram): all fields supported. - **TikTok**: status, budget, targeting (via `/v2/adgroup/update/`), and creative (via `/v2/ad/update/` patch-style: `headline` is ignored, `body` becomes `ad_text`). - **Google**: status, budget, KEYWORD edits via `targeting.keywords` / `targeting.negativeKeywords`, DEVICE bid adjustments via `targeting.devices`, LOCATION edits via `targeting.locations` (or the equivalent top-level `targeting.countries` / `regions` / `cities` / `zips` / `metros`), and LANGUAGE edits via `targeting.languages`. Each list you send becomes the FULL new set of its kind (criteria not in the list are removed, except devices, which Google cannot remove and which are switched off with a bid modifier of 0 instead); a kind left out is untouched. Any other `targeting` field returns 400: Google cannot mutate it post-create without recreating the campaign. Creative edits are dispatched on the ad's `advertisingChannelType`, and every supported field replaces a whole set; a field you omit is preserved. - **Search**: top-level `headlines`, `descriptions` and `finalUrls`. Use 3-15 headlines (1-30 characters) and 2-4 descriptions (1-90 characters). Omit an asset to remove it; omit pinnedField on an included asset to unpin it. Updates do not pad or truncate text. The legacy creative fields remain unsupported. - **Display**: top-level `headlines` (1-5, no pinnedField, display ads have no pinned positions), `descriptions` (1-5) and `finalUrls`, plus `creative.longHeadline`, `creative.businessName`, `creative.imageUrl` (the landscape marketing image) and `creative.squareImageUrl`. Each image URL is uploaded as a new Google asset and the ad is pointed at it; Google assets are immutable, so the previous asset stays in the account's asset library. - **Performance Max**: top-level `assetGroup`, which swaps asset roles on the ad's asset group. The other creative fields return 422 for this channel, and `assetGroup` returns 422 on any other channel. - **LinkedIn**: status, budget, targeting (countries or regions, excludedLocations (countries), the B2B facets, and audience segments; applied to the LinkedIn Campaign via PARTIAL_UPDATE, and REPLACES the campaign's entire targetingCriteria, not a merge), and creative (uploads new media, creates a replacement inline creative on the same campaign, pauses the old one). - **Pinterest / X / OpenAI Ads**: status + budget only. Sending `targeting` or `creative` returns 501 with code `unsupported_platform_operation`. OpenAI Ads budget is lifetime-only (see `budget.type` below). **Google location and language replacement:** locations, languages and devices are campaign-level criteria on Google, so these edits apply to every ad group and ad in the ad's campaign. Send the complete list you want to keep. Zernio diffs it against the campaign's live criteria and sends the removes and the creates in ONE `googleAds:mutate`, so the campaign is never left with a half-applied set; criteria already in the list keep their criterion ID and history. Excluded (negative) locations are left untouched. Two cases are refused rather than applied: an empty location list returns 400 (a Google campaign with no location criteria targets every country, which is never what \"remove my locations\" means, so omit the field instead), and radius targeting (`customLocations`) returns 422 because it is a separate Google criterion type that this replacement neither creates nor removes. Send either `targeting.locations` or the top-level geo fields, not both: mixing them returns 400. **Google keyword replacement:** These edits affect the ad's entire ad group, including sibling ads. Positive (`targeting.keywords`) and negative (`targeting.negativeKeywords`) sets are independent: omit a field to leave that set unchanged, or send `[]` to remove every keyword of that kind. Zernio compares each supplied set with Google's live criteria by case-insensitive keyword text and match type. A matching criterion is left untouched, retaining its criterion ID, enabled/paused status, keyword-level bid overrides, labels, and criterion-associated history/statistics. Zernio does not reset its quality score; Google continues to calculate scores and statistics normally. Text comparison does not trim whitespace. A bare string or an object without `matchType` means `broad`, not the existing criterion's match type. For example, resending an existing `{ \"text\": \"plumber\", \"matchType\": \"exact\" }` preserves it; sending `\"plumber\"` instead removes that EXACT criterion and requests a BROAD one. Changing text or match type removes criteria no longer requested and creates any missing criteria. New criteria get new IDs and do not inherit removed criteria's bid overrides, labels, or history. Historical reporting for a removed criterion is not transferred to its replacement. To add keywords without replacing a set, use [POST /v1/ads/keywords](https://docs.zernio.com/ad-campaigns/add-ad-keywords). Use `PATCH /v1/ads/keywords/{keywordId}` to pause/enable one keyword, or `DELETE /v1/ads/keywords/{keywordId}` to remove it.
|
|
2773
2773
|
|
|
2774
2774
|
### Examples
|
|
2775
2775
|
|
|
@@ -2911,7 +2911,7 @@ end
|
|
|
2911
2911
|
|
|
2912
2912
|
Pause or resume a campaign
|
|
2913
2913
|
|
|
2914
|
-
Writes the campaign's own on/off switch, then lets the platform cascade delivery to its ad sets and ads. Makes one platform API call, not one per ad. The switch is always written, whatever delivery status the ads underneath report: an ad still in review does not block resuming its campaign. The echoed `status` is the confirmation that it landed. `updated` / `skipped` describe only the ads whose own stored status CHANGED alongside it, so `updated: 0` is a normal successful response, not a no-op. Ads are skipped when they are in a terminal status (rejected, completed, cancelled), already in the target state, or switched on but not yet delivering. The last group keeps its `pending_review` / `error` status until the platform reports what it became. `skippedReasons` names which case applies. On Meta this flips the campaign only. An ad set paused in its own right stays paused, so pair this with PUT /v1/ads/ad-sets/{adSetId}/status when you also need the ad set switched back on.
|
|
2914
|
+
Writes the campaign's own on/off switch, then lets the platform cascade delivery to its ad sets and ads. Makes one platform API call, not one per ad. The switch is always written, whatever delivery status the ads underneath report: an ad still in review does not block resuming its campaign. The echoed `status` is the confirmation that it landed. `updated` / `skipped` describe only the ads whose own stored status CHANGED alongside it, so `updated: 0` is a normal successful response, not a no-op. Ads are skipped when they are in a terminal status (rejected, completed, cancelled), already in the target state, or switched on but not yet delivering. The last group keeps its `pending_review` / `error` status until the platform reports what it became. `skippedReasons` names which case applies. On Meta this flips the campaign only. An ad set paused in its own right stays paused, so pair this with PUT /v1/ads/ad-sets/{adSetId}/status when you also need the ad set switched back on. Google keeps an independent on/off switch at campaign, ad group and ad level and the most restrictive one wins, so `active` switches the campaign on TOGETHER with the ad groups and ads Zernio tracks under it, in one mutate. Without that the campaign reads ENABLED while a paused ad group or ad keeps it from serving. `paused` writes the campaign alone, which already stops delivery and leaves each ad's own switch as you set it.
|
|
2915
2915
|
|
|
2916
2916
|
### Examples
|
|
2917
2917
|
|
|
@@ -3479,7 +3479,7 @@ end
|
|
|
3479
3479
|
|
|
3480
3480
|
Edit a Google campaign's device, location, or language targeting
|
|
3481
3481
|
|
|
3482
|
-
Google Ads compliance row M.10: geo and language targeting set at creation must stay editable afterwards. Send at least one of `devices`, `locations`, `languages`; each provided field REPLACES that field's existing criteria on the campaign (a full set, not a delta). Fields left out of the body are untouched. Google only; every other platform returns 501. `locations` accepts the same shapes as campaign creation: a bare array of ISO country codes, or an object with `countries`/`regions`/`cities`/`zips`/`metros` key lists (`key` from GET /v1/ads/targeting/search?dimension=geo). Negative (excluded) locations are left untouched by this endpoint. An empty location list returns 400 instead of removing every criterion: a Google campaign with no location criteria targets every country, so omit `locations` to leave targeting alone. The removes and the creates go out in ONE Google `googleAds:mutate`, so a failed edit leaves the campaign's previous set intact rather than a half-applied one. `languages` is an array of Google's language codes (ISO 639-1, plus variants such as `zh_CN`); an unknown code returns 400. The response includes the refreshed `devices`/`locations`/`languages` state read back from Google after the edit, and invalidates the cached copy `GET` on this campaign would otherwise keep serving.
|
|
3482
|
+
Google Ads compliance row M.10: geo and language targeting set at creation must stay editable afterwards. Send at least one of `devices`, `locations`, `languages`; each provided field REPLACES that field's existing criteria on the campaign (a full set, not a delta). Fields left out of the body are untouched. Google only; every other platform returns 501. `devices` is the full set of device bid modifiers: a supported device you leave out is switched off with a bid modifier of 0, since Google cannot remove a device criterion. A device the campaign's channel does not carry, and a set that switches every device off, both return 422. `locations` accepts the same shapes as campaign creation: a bare array of ISO country codes, or an object with `countries`/`regions`/`cities`/`zips`/`metros` key lists (`key` from GET /v1/ads/targeting/search?dimension=geo). Negative (excluded) locations are left untouched by this endpoint. An empty location list returns 400 instead of removing every criterion: a Google campaign with no location criteria targets every country, so omit `locations` to leave targeting alone. The removes and the creates go out in ONE Google `googleAds:mutate`, so a failed edit leaves the campaign's previous set intact rather than a half-applied one. `languages` is an array of Google's language codes (ISO 639-1, plus variants such as `zh_CN`); an unknown code returns 400. The response includes the refreshed `devices`/`locations`/`languages` state read back from Google after the edit, and invalidates the cached copy `GET` on this campaign would otherwise keep serving.
|
|
3483
3483
|
|
|
3484
3484
|
### Examples
|
|
3485
3485
|
|
data/docs/ConnectApi.md
CHANGED
|
@@ -1062,7 +1062,7 @@ end
|
|
|
1062
1062
|
|
|
1063
1063
|
Get OAuth connect URL
|
|
1064
1064
|
|
|
1065
|
-
Initiate an OAuth connection flow. Returns an authUrl to redirect the user to. Standard flow: Zernio hosts the selection UI, then redirects to your redirect_url. Headless mode (headless=true): user is redirected to your redirect_url with OAuth data for custom UI. Use the platform-specific selection endpoints to complete.
|
|
1065
|
+
Initiate an OAuth connection flow. Returns an authUrl to redirect the user to. Standard flow: Zernio hosts the selection UI, then redirects to your redirect_url. Headless mode (headless=true): user is redirected to your redirect_url with OAuth data for custom UI. Use the platform-specific selection endpoints to complete. TikTok: every connection now goes through the TikTok for Business app. One TikTok account per profile, so connecting on a profile that already holds one replaces it. Reconnecting the SAME account keeps it and all of its history; authorizing a DIFFERENT TikTok account takes the slot over and permanently deletes the previous account's analytics, inbox and DM history. The two are told apart by the `@handle` stored at the last connect, so an account whose handle has been renamed on TikTok since then reads as a different account. An authorization that leaves out a permission the connected account needs changes nothing at all and comes back as `missing_tiktok_permissions`; connect again and accept every permission on TikTok's screen.
|
|
1066
1066
|
|
|
1067
1067
|
### Examples
|
|
1068
1068
|
|
|
@@ -1079,7 +1079,7 @@ api_instance = Zernio::ConnectApi.new
|
|
|
1079
1079
|
platform = 'facebook' # String | Social media platform to connect. `snapchat` is a closed beta with no public release date: it returns 403 `PLATFORM_BETA_RESTRICTED` until the account is approved.
|
|
1080
1080
|
profile_id = 'profile_id_example' # String | Your Zernio profile ID (get from /v1/profiles). For WhatsApp, a Zernio-provisioned number can only be connected on the profile it was provisioned to; connecting from any other profile is rejected with a 409.
|
|
1081
1081
|
opts = {
|
|
1082
|
-
redirect_url: 'redirect_url_example', # String | Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, platform_requires_destination,
|
|
1082
|
+
redirect_url: 'redirect_url_example', # String | Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, missing_tiktok_permissions, platform_requires_destination, reconnect_account_mismatch, invalid_request Access and limits: profile_not_found, invalid_profile_id, access_denied, account_limit_exceeded, profile_limit_exceeded, payment_required Destination selection: no_facebook_pages, facebook_pages_error, no_google_locations, google_locations_error, google_permission_denied, no_snapchat_public_profiles, snapchat_profiles_error, discord_no_guild, slack_no_team WhatsApp: whatsapp_error, one_whatsapp_per_profile, whatsapp_number_already_connected, whatsapp_number_pinned_to_profile, connection_cancelled Google Ads (platform=googleads): google_ads_auth_failed, google_ads_invalid_state, google_ads_config_error, google_ads_token_failed, google_ads_quota_exhausted, google_ads_callback_error TikTok Ads (platform=tiktokads): tiktok_ads_auth_failed, tiktok_ads_invalid_state, tiktok_ads_access_denied, tiktok_ads_config_error, tiktok_ads_token_failed, tiktok_ads_account_not_found, tiktok_ads_callback_error X Ads (platform=xads): x_ads_denied, x_ads_auth_failed, x_ads_config_error, x_ads_account_not_found, x_ads_state_error, x_ads_token_failed, x_ads_token_missing, x_ads_callback_error Shopify (platform=shopify): shopify_auth_failed, shopify_config_error, shopify_invalid_state, shopify_invalid_hmac, shopify_invalid_shop, shopify_missing_scopes, shopify_callback_error 1. On this endpoint every upstream OAuth error is collapsed into `oauth_denied`. The provider's own value (for example Meta's `access_denied`) is not forwarded. The dedicated ads flows below are different: they use their own denial slugs and `google_ads_auth_failed` and `tiktok_ads_auth_failed` may carry the provider's raw error string in `error_message`. 2. On the tiktok and twitter ads flows `platform` carries the ads platform id (`tiktokads`, `xads`), not the value used in the request path. The googleads and shopify flows report `googleads` and `shopify`. 3. `missing_tiktok_permissions` means the TikTok authorization left out a permission the already-connected account needs, so nothing was changed and it keeps working as before. It is user-fixable: connect again and accept every permission on TikTok's screen.
|
|
1083
1083
|
headless: true, # Boolean | When true, the user is redirected to your redirect_url with raw OAuth data (code, state) instead of Zernio's default account selection UI. Use this to build a custom connect experience.
|
|
1084
1084
|
login_method: 'instagram_login', # String | Instagram only. Which of the two Instagram connection methods to use. Ignored for every other platform. `instagram_login` (the default, and what you get if you omit this): the Instagram Login dialog. The user authorizes their Instagram professional account directly, no Facebook Page required. `facebook_login`: the Facebook Login dialog, i.e. \"Instagram API with Facebook Login\". The user authorizes a Facebook Page that has a linked Instagram professional account, and every API call for that account then runs through the Page. Use this when the customer manages Instagram through a Page and expects the Facebook consent screen. Because the user has to pick which Page to connect, the callback continues at the account-selection step, `/v1/connect/instagram/select-account`. `facebook_login` supports `headless=true` like the other selection platforms: the callback redirects to your `redirect_url` with `profileId`, `tempToken`, `platform=instagram`, `step=select_account` and `connect_token`, which you pass into the select-account endpoints to finish. The default `instagram_login` has no selection step, so it connects the account directly.
|
|
1085
1085
|
onboarding: 'api', # String | WhatsApp only. Ignored for every other platform. Controls which screen Meta's Embedded Signup popup shows. If omitted, the connection defaults to coexistence (same as `business_app` below), preserving existing behavior for numbers already on the WhatsApp Business app. `api`: standard Embedded Signup, showing Meta's WABA/number picker. Use this to connect a phone number already on Cloud API elsewhere. `business_app`: coexistence, i.e. 'Connect existing WhatsApp Business app' (a number shared between Cloud API and the consumer WhatsApp Business app).
|
|
@@ -1122,7 +1122,7 @@ end
|
|
|
1122
1122
|
| ---- | ---- | ----------- | ----- |
|
|
1123
1123
|
| **platform** | **String** | Social media platform to connect. `snapchat` is a closed beta with no public release date: it returns 403 `PLATFORM_BETA_RESTRICTED` until the account is approved. | |
|
|
1124
1124
|
| **profile_id** | **String** | Your Zernio profile ID (get from /v1/profiles). For WhatsApp, a Zernio-provisioned number can only be connected on the profile it was provisioned to; connecting from any other profile is rejected with a 409. | |
|
|
1125
|
-
| **redirect_url** | **String** | Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, platform_requires_destination,
|
|
1125
|
+
| **redirect_url** | **String** | Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, missing_tiktok_permissions, platform_requires_destination, reconnect_account_mismatch, invalid_request Access and limits: profile_not_found, invalid_profile_id, access_denied, account_limit_exceeded, profile_limit_exceeded, payment_required Destination selection: no_facebook_pages, facebook_pages_error, no_google_locations, google_locations_error, google_permission_denied, no_snapchat_public_profiles, snapchat_profiles_error, discord_no_guild, slack_no_team WhatsApp: whatsapp_error, one_whatsapp_per_profile, whatsapp_number_already_connected, whatsapp_number_pinned_to_profile, connection_cancelled Google Ads (platform=googleads): google_ads_auth_failed, google_ads_invalid_state, google_ads_config_error, google_ads_token_failed, google_ads_quota_exhausted, google_ads_callback_error TikTok Ads (platform=tiktokads): tiktok_ads_auth_failed, tiktok_ads_invalid_state, tiktok_ads_access_denied, tiktok_ads_config_error, tiktok_ads_token_failed, tiktok_ads_account_not_found, tiktok_ads_callback_error X Ads (platform=xads): x_ads_denied, x_ads_auth_failed, x_ads_config_error, x_ads_account_not_found, x_ads_state_error, x_ads_token_failed, x_ads_token_missing, x_ads_callback_error Shopify (platform=shopify): shopify_auth_failed, shopify_config_error, shopify_invalid_state, shopify_invalid_hmac, shopify_invalid_shop, shopify_missing_scopes, shopify_callback_error 1. On this endpoint every upstream OAuth error is collapsed into `oauth_denied`. The provider's own value (for example Meta's `access_denied`) is not forwarded. The dedicated ads flows below are different: they use their own denial slugs and `google_ads_auth_failed` and `tiktok_ads_auth_failed` may carry the provider's raw error string in `error_message`. 2. On the tiktok and twitter ads flows `platform` carries the ads platform id (`tiktokads`, `xads`), not the value used in the request path. The googleads and shopify flows report `googleads` and `shopify`. 3. `missing_tiktok_permissions` means the TikTok authorization left out a permission the already-connected account needs, so nothing was changed and it keeps working as before. It is user-fixable: connect again and accept every permission on TikTok's screen. | [optional] |
|
|
1126
1126
|
| **headless** | **Boolean** | When true, the user is redirected to your redirect_url with raw OAuth data (code, state) instead of Zernio's default account selection UI. Use this to build a custom connect experience. | [optional][default to false] |
|
|
1127
1127
|
| **login_method** | **String** | Instagram only. Which of the two Instagram connection methods to use. Ignored for every other platform. `instagram_login` (the default, and what you get if you omit this): the Instagram Login dialog. The user authorizes their Instagram professional account directly, no Facebook Page required. `facebook_login`: the Facebook Login dialog, i.e. \"Instagram API with Facebook Login\". The user authorizes a Facebook Page that has a linked Instagram professional account, and every API call for that account then runs through the Page. Use this when the customer manages Instagram through a Page and expects the Facebook consent screen. Because the user has to pick which Page to connect, the callback continues at the account-selection step, `/v1/connect/instagram/select-account`. `facebook_login` supports `headless=true` like the other selection platforms: the callback redirects to your `redirect_url` with `profileId`, `tempToken`, `platform=instagram`, `step=select_account` and `connect_token`, which you pass into the select-account endpoints to finish. The default `instagram_login` has no selection step, so it connects the account directly. | [optional][default to 'instagram_login'] |
|
|
1128
1128
|
| **onboarding** | **String** | WhatsApp only. Ignored for every other platform. Controls which screen Meta's Embedded Signup popup shows. If omitted, the connection defaults to coexistence (same as `business_app` below), preserving existing behavior for numbers already on the WhatsApp Business app. `api`: standard Embedded Signup, showing Meta's WABA/number picker. Use this to connect a phone number already on Cloud API elsewhere. `business_app`: coexistence, i.e. 'Connect existing WhatsApp Business app' (a number shared between Cloud API and the consumer WhatsApp Business app). | [optional] |
|
data/docs/CreatePostRequest.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| **scheduled_for** | **Time** | When to publish. Required unless `publishNow` is true, `queuedFromProfile` is set, or the post is a draft. An ISO 8601 value with a `Z` or offset (`2026-01-15T10:00:00Z`, `2026-01-15T11:00:00+01:00`) is taken as-is; a value without one (`2026-01-15T10:00:00` or `2026-01-15 10:00`) is read as local time in `timezone`. A value already in the past is published synchronously in the same request. Ignored when `publishNow` is true. | [optional] |
|
|
12
12
|
| **publish_now** | **Boolean** | Publish to every platform synchronously in this request instead of scheduling; the response then carries each platform result and `platformPostUrl`, with HTTP 207 when some platforms failed. Takes precedence over `scheduledFor`; ignored when `isDraft` is true. | [optional][default to false] |
|
|
13
13
|
| **is_draft** | **Boolean** | When true, saves the post as a draft. When none of scheduledFor, publishNow, or queuedFromProfile are provided, the post defaults to draft automatically. | [optional][default to false] |
|
|
14
|
-
| **dry_run** | **Boolean** | TikTok only. Preview whether each `tiktok` entry in `platforms` could publish right now under the TikTok Direct Post daily limits, without creating, scheduling or publishing anything: no post is persisted and no upload slot is claimed, so it can be repeated freely. The request still goes through auth, the payment gate and body validation, then returns HTTP 200 with `{ dryRun: true, canPublish, tiktok: [...] }` instead of 201. Only `tiktok` entries are evaluated; other platforms in the body are ignored, and a body with no `tiktok` entry is rejected with 400 `invalid_field_value` on `platforms`. An entry with `platformSpecificData.tiktokSettings.draft: true` (Creator Inbox upload) is not subject to the limit and always reports `canPublish: true`. | [optional][default to false] |
|
|
14
|
+
| **dry_run** | **Boolean** | TikTok only. Preview whether each `tiktok` entry in `platforms` could publish right now under the TikTok Direct Post daily limits, without creating, scheduling or publishing anything: no post is persisted and no upload slot is claimed, so it can be repeated freely. The request still goes through auth, the payment gate and body validation, then returns HTTP 200 with `{ dryRun: true, canPublish, tiktok: [...] }` instead of 201. Only `tiktok` entries are evaluated; other platforms in the body are ignored, and a body with no `tiktok` entry is rejected with 400 `invalid_field_value` on `platforms`. An entry with `platformSpecificData.tiktokSettings.draft: true` (Creator Inbox upload) is not subject to the limit and always reports `canPublish: true`. Accounts connected through the TikTok for Business app do not go through these limits at all and also always report `canPublish: true`, so on those accounts a dry run confirms the request is well-formed rather than gating it. | [optional][default to false] |
|
|
15
15
|
| **timezone** | **String** | IANA timezone (`Europe/Madrid`, `America/New_York`) used to interpret a `scheduledFor` (root or per-platform) that carries no `Z` or offset. Has no effect on values that already carry one. An unknown name returns 400 when `scheduledFor` is set. | [optional][default to 'UTC'] |
|
|
16
16
|
| **tags** | **Array<String>** | Tags/keywords. YouTube constraints: each tag max 100 chars, combined max 500 chars, duplicates auto-removed. | [optional] |
|
|
17
17
|
| **hashtags** | **Array<String>** | Stored for reference only. Hashtags are NOT automatically appended to the caption when publishing. Include hashtags directly in the content field (platforms like Instagram only support hashtags as caption text). For YouTube keywords, use the tags field instead. | [optional] |
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
| ---- | ---- | ----------- | ----- |
|
|
7
7
|
| **node** | **String** | | [optional] |
|
|
8
8
|
| **status** | **String** | | [optional] |
|
|
9
|
-
| **reason** | **String** | Why the node could not be validated (
|
|
9
|
+
| **reason** | **String** | Why the node could not be validated (on skipped), or what the dry run could not check and what the request would do as sent (on validated). A Performance Max validation with no location targeting reports here that the campaign would run worldwide. | [optional] |
|
|
10
10
|
|
|
11
11
|
## Example
|
|
12
12
|
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
| **validate_only** | **Boolean** | Google Performance Max validates the complete atomic campaign and asset group with no resource creation or local persistence. Google validation still downloads image URLs and consumes quota. On Meta, 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, all-image placementAssets with per-rule copy, existing video.id or existingCreativeId; other media pools, new video uploads, creatives[], adSetId and RESERVED buying return 400. Placement validation uses existing Instagram identities only. Existing campaign or creative nodes are marked skipped. Success returns 200 with per-node results; Meta rejection returns an error. | [optional] |
|
|
23
23
|
| **budget_amount** | **Float** | 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 and Performance Max shapes. Inherited on attach. OpenAI Ads requires a $1 minimum (its budget is lifetime-only, see budgetType). | [optional] |
|
|
24
24
|
| **budget_type** | **String** | Required on legacy, multi-creative and Performance Max 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. | [optional] |
|
|
25
|
-
| **status** | **String** | Google Performance Max accepts PAUSED only and always creates a paused campaign. Meta, TikTok, and LinkedIn: publish state of the created entities. Omitted or ACTIVE publishes live (default, back-compat); PAUSED creates them paused so you can review before they spend. On Meta the pause is held on the campaign this call creates, leaving the ad set and ad switched on, so a single PUT /v1/ads/campaigns/{campaignId}/status with `active` brings the whole thing live. It is held at every level instead when the pause cannot rely on the campaign: `existingCampaignId` (that campaign may be running and is never touched) or `campaignStatus: ACTIVE`. On TikTok the whole campaign > ad group > ad hierarchy stays paused. On LinkedIn the whole campaign group, campaign, and creative hierarchy stays PAUSED (intendedStatus PAUSED on each). | [optional] |
|
|
26
|
-
| **campaign_status** | **String** | Meta
|
|
25
|
+
| **status** | **String** | Google Performance Max accepts PAUSED only and always creates a paused campaign. Google Search and Display, Meta, TikTok, and LinkedIn: publish state of the created entities. Omitted or ACTIVE publishes live (default, back-compat); PAUSED creates them paused so you can review before they spend. On Meta the pause is held on the campaign this call creates, leaving the ad set and ad switched on, so a single PUT /v1/ads/campaigns/{campaignId}/status with `active` brings the whole thing live. It is held at every level instead when the pause cannot rely on the campaign: `existingCampaignId` (that campaign may be running and is never touched) or `campaignStatus: ACTIVE`. Google Search and Display follow the same rule, and because Google keeps an independent switch at campaign, ad group and ad level, a PAUSED create leaves the campaign it creates PAUSED at Google. On TikTok the whole campaign > ad group > ad hierarchy stays paused. On LinkedIn the whole campaign group, campaign, and creative hierarchy stays PAUSED (intendedStatus PAUSED on each). | [optional] |
|
|
26
|
+
| **campaign_status** | **String** | Meta and Google. Overrides `status` for the campaign level alone, so you can create a live campaign whose ad set and ad stay paused, or the reverse. Omitted, it follows `status`. | [optional] |
|
|
27
27
|
| **budget_level** | **String** | Meta only. Where the budget lives, which selects the Meta budget model: - `adset` (default): ABO (Ad-set Budget Optimization). The budget is set on the ad set. This is the back-compatible behaviour; omit this field to keep it. - `campaign`: CBO (Campaign Budget Optimization / Advantage Campaign Budget). The budget AND `bidStrategy` are set on the CAMPAIGN, and Meta distributes spend across ad sets automatically. The returned ad stores the applied `budgetLevel` and budget in `campaignBudget` for CBO or `adSetBudget` for ABO. Edit CBO budgets with `PUT /v1/ads/campaigns/{campaignId}` and ABO budgets with `PUT /v1/ads/ad-sets/{adSetId}`. Meta requires the budget at exactly one level, never both. Non-Meta platforms ignore this field. Ignored on the attach shape (`adSetId`), which inherits the existing budget. | [optional][default to 'adset'] |
|
|
28
28
|
| **currency** | **String** | ISO 4217 currency code matching the ad account's currency (e.g. `USD`). 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). | [optional] |
|
|
29
29
|
| **headline** | **String** | Required for Meta, Google, Pinterest, LinkedIn, and OpenAI Ads on legacy + attach shapes (skip for multi-creative; use `creatives[].headline`). Ignored for TikTok and X. Max: Meta=255, Google=30, Pinterest=100, LinkedIn=400, OpenAI=50 (min 3). On LinkedIn this is the ad's headline (the bold text on the creative); for traffic ads it's the link card title. On OpenAI Ads this is the chat card's title. | [optional] |
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
| ---- | ---- | ----------- | ----- |
|
|
7
7
|
| **device** | **String** | | [optional] |
|
|
8
8
|
| **included** | **Boolean** | | [optional] |
|
|
9
|
-
| **bid_modifier** | **Float** |
|
|
9
|
+
| **bid_modifier** | **Float** | Google's bid adjustment for this device: null when it has none, 0 when the device is switched off, otherwise 0.1 to 10. | [optional] |
|
|
10
10
|
|
|
11
11
|
## Example
|
|
12
12
|
|
data/docs/TikTokPlatformData.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
| **express_consent_given** | **Boolean** | User has given express consent for posting | [optional] |
|
|
17
17
|
| **media_type** | **String** | Optional override. Defaults based on provided media items. | [optional] |
|
|
18
18
|
| **video_cover_timestamp_ms** | **Integer** | Optional for video posts. Timestamp in milliseconds to select which frame to use as thumbnail (defaults to 1000ms/1 second). Ignored when videoCoverImageUrl is provided. | [optional] |
|
|
19
|
-
| **video_cover_image_url** | **String** | Optional for video posts. URL of a custom thumbnail image (JPG, PNG, or WebP, max 20MB). The image is stitched as a single frame at the start of the video
|
|
19
|
+
| **video_cover_image_url** | **String** | Optional for video posts. URL of a custom thumbnail image (JPG, PNG, or WebP, max 20MB). Any downloadable URL works: we rehost it ourselves. The image is stitched as a single frame at the start of the video to serve as the cover. Accounts connected through the TikTok for Business app hand it to TikTok as the cover instead, with no stitching, falling back to videoCoverTimestampMs without it. Overrides videoCoverTimestampMs when provided. | [optional] |
|
|
20
20
|
| **photo_cover_index** | **Integer** | Optional for photo carousels. Index of image to use as cover, 0-based (defaults to 0/first image). | [optional] |
|
|
21
21
|
| **auto_add_music** | **Boolean** | When true, TikTok may add recommended music (photos only) | [optional] |
|
|
22
22
|
| **video_made_with_ai** | **Boolean** | Set true to disclose AI-generated content. Accounts connected through the TikTok for Business app carry the disclosure on video posts only: the business photo endpoint has no AI disclosure field, so true on a direct photo post is rejected at creation rather than published undisclosed. Send draft true to publish such a photo post and set the disclosure in the TikTok app. | [optional] |
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
| ---- | ---- | ----------- | ----- |
|
|
7
7
|
| **keywords** | [**Array<UpdateAdRequestTargetingKeywordsInner>**](UpdateAdRequestTargetingKeywordsInner.md) | Google only. The FULL desired set of positive keywords for the entire ad group. Omit to leave positives unchanged; [] removes all positives. Negatives are independent. Entries are strings (BROAD) or { text, matchType } with matchType exact | phrase | broad; an omitted matchType also defaults to BROAD. Matching case-insensitive text AND match type retains the existing criterion ID, status, bid overrides, labels and history without a mutation. A changed text or match type uses remove/create, without transferring the old criterion's attributes or history. See Google keyword replacement above for an EXACT-to-BROAD example. Mirrored to GET /v1/ads/keywords immediately. | [optional] |
|
|
8
8
|
| **negative_keywords** | [**Array<UpdateAdRequestTargetingKeywordsInner>**](UpdateAdRequestTargetingKeywordsInner.md) | Google only. The FULL desired set of negative keywords for the entire ad group, independent of positives. Omit to leave negatives unchanged; [] removes all negatives. Uses the same text/match-type identity and preservation contract as keywords above. Strings and objects without matchType default to BROAD, so resending an EXACT or PHRASE negative as a bare string requests a different criterion. Campaign negatives are separate: use /v1/ads/campaigns/{campaignId}/negative-keywords to manage those. | [optional] |
|
|
9
|
-
| **devices** | [**Array<UpdateAdRequestTargetingDevicesInner>**](UpdateAdRequestTargetingDevicesInner.md) | Google only. The FULL new set of device
|
|
9
|
+
| **devices** | [**Array<UpdateAdRequestTargetingDevicesInner>**](UpdateAdRequestTargetingDevicesInner.md) | Google only. The FULL new set of device bid modifiers for the campaign. Entries are a device name alone (targeted, bid modifier reset to 1) or { device, bidModifier }. A supported device you leave out is switched off, written as a bid modifier of 0. Google never removes a device criterion, so an excluded device reads back as bidModifier 0 rather than disappearing, and a set that switches every device off returns 422. Which devices a campaign carries depends on its channel: Search campaigns have MOBILE, DESKTOP and TABLET, Display campaigns also have CONNECTED_TV, and sending a device the campaign does not carry returns 422. | [optional] |
|
|
10
10
|
| **age_min** | **Integer** | | [optional] |
|
|
11
11
|
| **age_max** | **Integer** | | [optional] |
|
|
12
12
|
| **countries** | **Array<String>** | ISO 3166-1 alpha-2 codes. On Google this is the FULL new country set for the campaign (same contract as `locations`); on LinkedIn it replaces the campaign's geo criteria. | [optional] |
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
| Name | Type | Description | Notes |
|
|
6
6
|
| ---- | ---- | ----------- | ----- |
|
|
7
7
|
| **device** | **String** | | |
|
|
8
|
-
| **bid_modifier** | **Float** | Google device bid modifier
|
|
8
|
+
| **bid_modifier** | **Float** | Google device bid modifier. 0 switches the device off (minus 100%); otherwise 0.1 to 10 (minus 90% to plus 900%). Google rejects any value between 0 and 0.1. | [optional] |
|
|
9
9
|
|
|
10
10
|
## Example
|
|
11
11
|
|
|
@@ -1566,7 +1566,7 @@ module Zernio
|
|
|
1566
1566
|
end
|
|
1567
1567
|
|
|
1568
1568
|
# Read a Google campaign's device, location, and language targeting
|
|
1569
|
-
# Google Ads compliance requires geo, language, budget, and bidding targeting set at creation to stay editable afterwards; this reads the campaign state so an integrator can build an editor around it. Cached for the quota window (10 minutes fresh, up to 7 days last-good), not always a live read. Google only; every other platform returns 501. `devices`
|
|
1569
|
+
# Google Ads compliance requires geo, language, budget, and bidding targeting set at creation to stay editable afterwards; this reads the campaign state so an integrator can build an editor around it. Cached for the quota window (10 minutes fresh, up to 7 days last-good), not always a live read. Google only; every other platform returns 501. `devices` lists the device criteria the campaign carries, which depends on its channel: Search campaigns have MOBILE, DESKTOP and TABLET, Display campaigns also have CONNECTED_TV. `bidModifier` is Google's bid adjustment for that device, `null` when it has none, and `0` when the device is switched off; `included` is false for exactly that case.
|
|
1570
1570
|
# @param campaign_id [String] Google platform campaign ID
|
|
1571
1571
|
# @param [Hash] opts the optional parameters
|
|
1572
1572
|
# @option opts [String] :platform Disambiguates when the same campaignId string exists on more than one connected platform.
|
|
@@ -1577,7 +1577,7 @@ module Zernio
|
|
|
1577
1577
|
end
|
|
1578
1578
|
|
|
1579
1579
|
# Read a Google campaign's device, location, and language targeting
|
|
1580
|
-
# Google Ads compliance requires geo, language, budget, and bidding targeting set at creation to stay editable afterwards; this reads the campaign state so an integrator can build an editor around it. Cached for the quota window (10 minutes fresh, up to 7 days last-good), not always a live read. Google only; every other platform returns 501. `devices`
|
|
1580
|
+
# Google Ads compliance requires geo, language, budget, and bidding targeting set at creation to stay editable afterwards; this reads the campaign state so an integrator can build an editor around it. Cached for the quota window (10 minutes fresh, up to 7 days last-good), not always a live read. Google only; every other platform returns 501. `devices` lists the device criteria the campaign carries, which depends on its channel: Search campaigns have MOBILE, DESKTOP and TABLET, Display campaigns also have CONNECTED_TV. `bidModifier` is Google's bid adjustment for that device, `null` when it has none, and `0` when the device is switched off; `included` is false for exactly that case.
|
|
1581
1581
|
# @param campaign_id [String] Google platform campaign ID
|
|
1582
1582
|
# @param [Hash] opts the optional parameters
|
|
1583
1583
|
# @option opts [String] :platform Disambiguates when the same campaignId string exists on more than one connected platform.
|
|
@@ -2904,7 +2904,7 @@ module Zernio
|
|
|
2904
2904
|
end
|
|
2905
2905
|
|
|
2906
2906
|
# Update ad
|
|
2907
|
-
# Patch one or more fields on an ad. Status, budget, targeting, and creative changes are propagated to the platform. Per-platform support: - **Meta** (Facebook + Instagram): all fields supported. - **TikTok**: status, budget, targeting (via `/v2/adgroup/update/`), and creative (via `/v2/ad/update/` patch-style: `headline` is ignored, `body` becomes `ad_text`). - **Google**: status, budget, KEYWORD edits via `targeting.keywords` / `targeting.negativeKeywords`, DEVICE bid adjustments via `targeting.devices`, LOCATION edits via `targeting.locations` (or the equivalent top-level `targeting.countries` / `regions` / `cities` / `zips` / `metros`), and LANGUAGE edits via `targeting.languages`. Each list you send becomes the FULL new set of its kind (criteria not in the list are removed); a kind left out is untouched.
|
|
2907
|
+
# Patch one or more fields on an ad. Status, budget, targeting, and creative changes are propagated to the platform. Per-platform support: - **Meta** (Facebook + Instagram): all fields supported. - **TikTok**: status, budget, targeting (via `/v2/adgroup/update/`), and creative (via `/v2/ad/update/` patch-style: `headline` is ignored, `body` becomes `ad_text`). - **Google**: status, budget, KEYWORD edits via `targeting.keywords` / `targeting.negativeKeywords`, DEVICE bid adjustments via `targeting.devices`, LOCATION edits via `targeting.locations` (or the equivalent top-level `targeting.countries` / `regions` / `cities` / `zips` / `metros`), and LANGUAGE edits via `targeting.languages`. Each list you send becomes the FULL new set of its kind (criteria not in the list are removed, except devices, which Google cannot remove and which are switched off with a bid modifier of 0 instead); a kind left out is untouched. Any other `targeting` field returns 400: Google cannot mutate it post-create without recreating the campaign. Creative edits are dispatched on the ad's `advertisingChannelType`, and every supported field replaces a whole set; a field you omit is preserved. - **Search**: top-level `headlines`, `descriptions` and `finalUrls`. Use 3-15 headlines (1-30 characters) and 2-4 descriptions (1-90 characters). Omit an asset to remove it; omit pinnedField on an included asset to unpin it. Updates do not pad or truncate text. The legacy creative fields remain unsupported. - **Display**: top-level `headlines` (1-5, no pinnedField, display ads have no pinned positions), `descriptions` (1-5) and `finalUrls`, plus `creative.longHeadline`, `creative.businessName`, `creative.imageUrl` (the landscape marketing image) and `creative.squareImageUrl`. Each image URL is uploaded as a new Google asset and the ad is pointed at it; Google assets are immutable, so the previous asset stays in the account's asset library. - **Performance Max**: top-level `assetGroup`, which swaps asset roles on the ad's asset group. The other creative fields return 422 for this channel, and `assetGroup` returns 422 on any other channel. - **LinkedIn**: status, budget, targeting (countries or regions, excludedLocations (countries), the B2B facets, and audience segments; applied to the LinkedIn Campaign via PARTIAL_UPDATE, and REPLACES the campaign's entire targetingCriteria, not a merge), and creative (uploads new media, creates a replacement inline creative on the same campaign, pauses the old one). - **Pinterest / X / OpenAI Ads**: status + budget only. Sending `targeting` or `creative` returns 501 with code `unsupported_platform_operation`. OpenAI Ads budget is lifetime-only (see `budget.type` below). **Google location and language replacement:** locations, languages and devices are campaign-level criteria on Google, so these edits apply to every ad group and ad in the ad's campaign. Send the complete list you want to keep. Zernio diffs it against the campaign's live criteria and sends the removes and the creates in ONE `googleAds:mutate`, so the campaign is never left with a half-applied set; criteria already in the list keep their criterion ID and history. Excluded (negative) locations are left untouched. Two cases are refused rather than applied: an empty location list returns 400 (a Google campaign with no location criteria targets every country, which is never what \"remove my locations\" means, so omit the field instead), and radius targeting (`customLocations`) returns 422 because it is a separate Google criterion type that this replacement neither creates nor removes. Send either `targeting.locations` or the top-level geo fields, not both: mixing them returns 400. **Google keyword replacement:** These edits affect the ad's entire ad group, including sibling ads. Positive (`targeting.keywords`) and negative (`targeting.negativeKeywords`) sets are independent: omit a field to leave that set unchanged, or send `[]` to remove every keyword of that kind. Zernio compares each supplied set with Google's live criteria by case-insensitive keyword text and match type. A matching criterion is left untouched, retaining its criterion ID, enabled/paused status, keyword-level bid overrides, labels, and criterion-associated history/statistics. Zernio does not reset its quality score; Google continues to calculate scores and statistics normally. Text comparison does not trim whitespace. A bare string or an object without `matchType` means `broad`, not the existing criterion's match type. For example, resending an existing `{ \"text\": \"plumber\", \"matchType\": \"exact\" }` preserves it; sending `\"plumber\"` instead removes that EXACT criterion and requests a BROAD one. Changing text or match type removes criteria no longer requested and creates any missing criteria. New criteria get new IDs and do not inherit removed criteria's bid overrides, labels, or history. Historical reporting for a removed criterion is not transferred to its replacement. To add keywords without replacing a set, use [POST /v1/ads/keywords](https://docs.zernio.com/ad-campaigns/add-ad-keywords). Use `PATCH /v1/ads/keywords/{keywordId}` to pause/enable one keyword, or `DELETE /v1/ads/keywords/{keywordId}` to remove it.
|
|
2908
2908
|
# @param ad_id [String]
|
|
2909
2909
|
# @param update_ad_request [UpdateAdRequest]
|
|
2910
2910
|
# @param [Hash] opts the optional parameters
|
|
@@ -2915,7 +2915,7 @@ module Zernio
|
|
|
2915
2915
|
end
|
|
2916
2916
|
|
|
2917
2917
|
# Update ad
|
|
2918
|
-
# Patch one or more fields on an ad. Status, budget, targeting, and creative changes are propagated to the platform. Per-platform support: - **Meta** (Facebook + Instagram): all fields supported. - **TikTok**: status, budget, targeting (via `/v2/adgroup/update/`), and creative (via `/v2/ad/update/` patch-style: `headline` is ignored, `body` becomes `ad_text`). - **Google**: status, budget, KEYWORD edits via `targeting.keywords` / `targeting.negativeKeywords`, DEVICE bid adjustments via `targeting.devices`, LOCATION edits via `targeting.locations` (or the equivalent top-level `targeting.countries` / `regions` / `cities` / `zips` / `metros`), and LANGUAGE edits via `targeting.languages`. Each list you send becomes the FULL new set of its kind (criteria not in the list are removed); a kind left out is untouched.
|
|
2918
|
+
# Patch one or more fields on an ad. Status, budget, targeting, and creative changes are propagated to the platform. Per-platform support: - **Meta** (Facebook + Instagram): all fields supported. - **TikTok**: status, budget, targeting (via `/v2/adgroup/update/`), and creative (via `/v2/ad/update/` patch-style: `headline` is ignored, `body` becomes `ad_text`). - **Google**: status, budget, KEYWORD edits via `targeting.keywords` / `targeting.negativeKeywords`, DEVICE bid adjustments via `targeting.devices`, LOCATION edits via `targeting.locations` (or the equivalent top-level `targeting.countries` / `regions` / `cities` / `zips` / `metros`), and LANGUAGE edits via `targeting.languages`. Each list you send becomes the FULL new set of its kind (criteria not in the list are removed, except devices, which Google cannot remove and which are switched off with a bid modifier of 0 instead); a kind left out is untouched. Any other `targeting` field returns 400: Google cannot mutate it post-create without recreating the campaign. Creative edits are dispatched on the ad's `advertisingChannelType`, and every supported field replaces a whole set; a field you omit is preserved. - **Search**: top-level `headlines`, `descriptions` and `finalUrls`. Use 3-15 headlines (1-30 characters) and 2-4 descriptions (1-90 characters). Omit an asset to remove it; omit pinnedField on an included asset to unpin it. Updates do not pad or truncate text. The legacy creative fields remain unsupported. - **Display**: top-level `headlines` (1-5, no pinnedField, display ads have no pinned positions), `descriptions` (1-5) and `finalUrls`, plus `creative.longHeadline`, `creative.businessName`, `creative.imageUrl` (the landscape marketing image) and `creative.squareImageUrl`. Each image URL is uploaded as a new Google asset and the ad is pointed at it; Google assets are immutable, so the previous asset stays in the account's asset library. - **Performance Max**: top-level `assetGroup`, which swaps asset roles on the ad's asset group. The other creative fields return 422 for this channel, and `assetGroup` returns 422 on any other channel. - **LinkedIn**: status, budget, targeting (countries or regions, excludedLocations (countries), the B2B facets, and audience segments; applied to the LinkedIn Campaign via PARTIAL_UPDATE, and REPLACES the campaign's entire targetingCriteria, not a merge), and creative (uploads new media, creates a replacement inline creative on the same campaign, pauses the old one). - **Pinterest / X / OpenAI Ads**: status + budget only. Sending `targeting` or `creative` returns 501 with code `unsupported_platform_operation`. OpenAI Ads budget is lifetime-only (see `budget.type` below). **Google location and language replacement:** locations, languages and devices are campaign-level criteria on Google, so these edits apply to every ad group and ad in the ad's campaign. Send the complete list you want to keep. Zernio diffs it against the campaign's live criteria and sends the removes and the creates in ONE `googleAds:mutate`, so the campaign is never left with a half-applied set; criteria already in the list keep their criterion ID and history. Excluded (negative) locations are left untouched. Two cases are refused rather than applied: an empty location list returns 400 (a Google campaign with no location criteria targets every country, which is never what \"remove my locations\" means, so omit the field instead), and radius targeting (`customLocations`) returns 422 because it is a separate Google criterion type that this replacement neither creates nor removes. Send either `targeting.locations` or the top-level geo fields, not both: mixing them returns 400. **Google keyword replacement:** These edits affect the ad's entire ad group, including sibling ads. Positive (`targeting.keywords`) and negative (`targeting.negativeKeywords`) sets are independent: omit a field to leave that set unchanged, or send `[]` to remove every keyword of that kind. Zernio compares each supplied set with Google's live criteria by case-insensitive keyword text and match type. A matching criterion is left untouched, retaining its criterion ID, enabled/paused status, keyword-level bid overrides, labels, and criterion-associated history/statistics. Zernio does not reset its quality score; Google continues to calculate scores and statistics normally. Text comparison does not trim whitespace. A bare string or an object without `matchType` means `broad`, not the existing criterion's match type. For example, resending an existing `{ \"text\": \"plumber\", \"matchType\": \"exact\" }` preserves it; sending `\"plumber\"` instead removes that EXACT criterion and requests a BROAD one. Changing text or match type removes criteria no longer requested and creates any missing criteria. New criteria get new IDs and do not inherit removed criteria's bid overrides, labels, or history. Historical reporting for a removed criterion is not transferred to its replacement. To add keywords without replacing a set, use [POST /v1/ads/keywords](https://docs.zernio.com/ad-campaigns/add-ad-keywords). Use `PATCH /v1/ads/keywords/{keywordId}` to pause/enable one keyword, or `DELETE /v1/ads/keywords/{keywordId}` to remove it.
|
|
2919
2919
|
# @param ad_id [String]
|
|
2920
2920
|
# @param update_ad_request [UpdateAdRequest]
|
|
2921
2921
|
# @param [Hash] opts the optional parameters
|
|
@@ -3052,7 +3052,7 @@ module Zernio
|
|
|
3052
3052
|
end
|
|
3053
3053
|
|
|
3054
3054
|
# Pause or resume a campaign
|
|
3055
|
-
# Writes the campaign's own on/off switch, then lets the platform cascade delivery to its ad sets and ads. Makes one platform API call, not one per ad. The switch is always written, whatever delivery status the ads underneath report: an ad still in review does not block resuming its campaign. The echoed `status` is the confirmation that it landed. `updated` / `skipped` describe only the ads whose own stored status CHANGED alongside it, so `updated: 0` is a normal successful response, not a no-op. Ads are skipped when they are in a terminal status (rejected, completed, cancelled), already in the target state, or switched on but not yet delivering. The last group keeps its `pending_review` / `error` status until the platform reports what it became. `skippedReasons` names which case applies. On Meta this flips the campaign only. An ad set paused in its own right stays paused, so pair this with PUT /v1/ads/ad-sets/{adSetId}/status when you also need the ad set switched back on.
|
|
3055
|
+
# Writes the campaign's own on/off switch, then lets the platform cascade delivery to its ad sets and ads. Makes one platform API call, not one per ad. The switch is always written, whatever delivery status the ads underneath report: an ad still in review does not block resuming its campaign. The echoed `status` is the confirmation that it landed. `updated` / `skipped` describe only the ads whose own stored status CHANGED alongside it, so `updated: 0` is a normal successful response, not a no-op. Ads are skipped when they are in a terminal status (rejected, completed, cancelled), already in the target state, or switched on but not yet delivering. The last group keeps its `pending_review` / `error` status until the platform reports what it became. `skippedReasons` names which case applies. On Meta this flips the campaign only. An ad set paused in its own right stays paused, so pair this with PUT /v1/ads/ad-sets/{adSetId}/status when you also need the ad set switched back on. Google keeps an independent on/off switch at campaign, ad group and ad level and the most restrictive one wins, so `active` switches the campaign on TOGETHER with the ad groups and ads Zernio tracks under it, in one mutate. Without that the campaign reads ENABLED while a paused ad group or ad keeps it from serving. `paused` writes the campaign alone, which already stops delivery and leaves each ad's own switch as you set it.
|
|
3056
3056
|
# @param campaign_id [String] Platform campaign ID
|
|
3057
3057
|
# @param update_ad_campaign_status_request [UpdateAdCampaignStatusRequest]
|
|
3058
3058
|
# @param [Hash] opts the optional parameters
|
|
@@ -3063,7 +3063,7 @@ module Zernio
|
|
|
3063
3063
|
end
|
|
3064
3064
|
|
|
3065
3065
|
# Pause or resume a campaign
|
|
3066
|
-
# Writes the campaign's own on/off switch, then lets the platform cascade delivery to its ad sets and ads. Makes one platform API call, not one per ad. The switch is always written, whatever delivery status the ads underneath report: an ad still in review does not block resuming its campaign. The echoed `status` is the confirmation that it landed. `updated` / `skipped` describe only the ads whose own stored status CHANGED alongside it, so `updated: 0` is a normal successful response, not a no-op. Ads are skipped when they are in a terminal status (rejected, completed, cancelled), already in the target state, or switched on but not yet delivering. The last group keeps its `pending_review` / `error` status until the platform reports what it became. `skippedReasons` names which case applies. On Meta this flips the campaign only. An ad set paused in its own right stays paused, so pair this with PUT /v1/ads/ad-sets/{adSetId}/status when you also need the ad set switched back on.
|
|
3066
|
+
# Writes the campaign's own on/off switch, then lets the platform cascade delivery to its ad sets and ads. Makes one platform API call, not one per ad. The switch is always written, whatever delivery status the ads underneath report: an ad still in review does not block resuming its campaign. The echoed `status` is the confirmation that it landed. `updated` / `skipped` describe only the ads whose own stored status CHANGED alongside it, so `updated: 0` is a normal successful response, not a no-op. Ads are skipped when they are in a terminal status (rejected, completed, cancelled), already in the target state, or switched on but not yet delivering. The last group keeps its `pending_review` / `error` status until the platform reports what it became. `skippedReasons` names which case applies. On Meta this flips the campaign only. An ad set paused in its own right stays paused, so pair this with PUT /v1/ads/ad-sets/{adSetId}/status when you also need the ad set switched back on. Google keeps an independent on/off switch at campaign, ad group and ad level and the most restrictive one wins, so `active` switches the campaign on TOGETHER with the ad groups and ads Zernio tracks under it, in one mutate. Without that the campaign reads ENABLED while a paused ad group or ad keeps it from serving. `paused` writes the campaign alone, which already stops delivery and leaves each ad's own switch as you set it.
|
|
3067
3067
|
# @param campaign_id [String] Platform campaign ID
|
|
3068
3068
|
# @param update_ad_campaign_status_request [UpdateAdCampaignStatusRequest]
|
|
3069
3069
|
# @param [Hash] opts the optional parameters
|
|
@@ -3654,7 +3654,7 @@ module Zernio
|
|
|
3654
3654
|
end
|
|
3655
3655
|
|
|
3656
3656
|
# Edit a Google campaign's device, location, or language targeting
|
|
3657
|
-
# Google Ads compliance row M.10: geo and language targeting set at creation must stay editable afterwards. Send at least one of `devices`, `locations`, `languages`; each provided field REPLACES that field's existing criteria on the campaign (a full set, not a delta). Fields left out of the body are untouched. Google only; every other platform returns 501. `locations` accepts the same shapes as campaign creation: a bare array of ISO country codes, or an object with `countries`/`regions`/`cities`/`zips`/`metros` key lists (`key` from GET /v1/ads/targeting/search?dimension=geo). Negative (excluded) locations are left untouched by this endpoint. An empty location list returns 400 instead of removing every criterion: a Google campaign with no location criteria targets every country, so omit `locations` to leave targeting alone. The removes and the creates go out in ONE Google `googleAds:mutate`, so a failed edit leaves the campaign's previous set intact rather than a half-applied one. `languages` is an array of Google's language codes (ISO 639-1, plus variants such as `zh_CN`); an unknown code returns 400. The response includes the refreshed `devices`/`locations`/`languages` state read back from Google after the edit, and invalidates the cached copy `GET` on this campaign would otherwise keep serving.
|
|
3657
|
+
# Google Ads compliance row M.10: geo and language targeting set at creation must stay editable afterwards. Send at least one of `devices`, `locations`, `languages`; each provided field REPLACES that field's existing criteria on the campaign (a full set, not a delta). Fields left out of the body are untouched. Google only; every other platform returns 501. `devices` is the full set of device bid modifiers: a supported device you leave out is switched off with a bid modifier of 0, since Google cannot remove a device criterion. A device the campaign's channel does not carry, and a set that switches every device off, both return 422. `locations` accepts the same shapes as campaign creation: a bare array of ISO country codes, or an object with `countries`/`regions`/`cities`/`zips`/`metros` key lists (`key` from GET /v1/ads/targeting/search?dimension=geo). Negative (excluded) locations are left untouched by this endpoint. An empty location list returns 400 instead of removing every criterion: a Google campaign with no location criteria targets every country, so omit `locations` to leave targeting alone. The removes and the creates go out in ONE Google `googleAds:mutate`, so a failed edit leaves the campaign's previous set intact rather than a half-applied one. `languages` is an array of Google's language codes (ISO 639-1, plus variants such as `zh_CN`); an unknown code returns 400. The response includes the refreshed `devices`/`locations`/`languages` state read back from Google after the edit, and invalidates the cached copy `GET` on this campaign would otherwise keep serving.
|
|
3658
3658
|
# @param campaign_id [String] Google platform campaign ID
|
|
3659
3659
|
# @param update_campaign_targeting_request [UpdateCampaignTargetingRequest]
|
|
3660
3660
|
# @param [Hash] opts the optional parameters
|
|
@@ -3665,7 +3665,7 @@ module Zernio
|
|
|
3665
3665
|
end
|
|
3666
3666
|
|
|
3667
3667
|
# Edit a Google campaign's device, location, or language targeting
|
|
3668
|
-
# Google Ads compliance row M.10: geo and language targeting set at creation must stay editable afterwards. Send at least one of `devices`, `locations`, `languages`; each provided field REPLACES that field's existing criteria on the campaign (a full set, not a delta). Fields left out of the body are untouched. Google only; every other platform returns 501. `locations` accepts the same shapes as campaign creation: a bare array of ISO country codes, or an object with `countries`/`regions`/`cities`/`zips`/`metros` key lists (`key` from GET /v1/ads/targeting/search?dimension=geo). Negative (excluded) locations are left untouched by this endpoint. An empty location list returns 400 instead of removing every criterion: a Google campaign with no location criteria targets every country, so omit `locations` to leave targeting alone. The removes and the creates go out in ONE Google `googleAds:mutate`, so a failed edit leaves the campaign's previous set intact rather than a half-applied one. `languages` is an array of Google's language codes (ISO 639-1, plus variants such as `zh_CN`); an unknown code returns 400. The response includes the refreshed `devices`/`locations`/`languages` state read back from Google after the edit, and invalidates the cached copy `GET` on this campaign would otherwise keep serving.
|
|
3668
|
+
# Google Ads compliance row M.10: geo and language targeting set at creation must stay editable afterwards. Send at least one of `devices`, `locations`, `languages`; each provided field REPLACES that field's existing criteria on the campaign (a full set, not a delta). Fields left out of the body are untouched. Google only; every other platform returns 501. `devices` is the full set of device bid modifiers: a supported device you leave out is switched off with a bid modifier of 0, since Google cannot remove a device criterion. A device the campaign's channel does not carry, and a set that switches every device off, both return 422. `locations` accepts the same shapes as campaign creation: a bare array of ISO country codes, or an object with `countries`/`regions`/`cities`/`zips`/`metros` key lists (`key` from GET /v1/ads/targeting/search?dimension=geo). Negative (excluded) locations are left untouched by this endpoint. An empty location list returns 400 instead of removing every criterion: a Google campaign with no location criteria targets every country, so omit `locations` to leave targeting alone. The removes and the creates go out in ONE Google `googleAds:mutate`, so a failed edit leaves the campaign's previous set intact rather than a half-applied one. `languages` is an array of Google's language codes (ISO 639-1, plus variants such as `zh_CN`); an unknown code returns 400. The response includes the refreshed `devices`/`locations`/`languages` state read back from Google after the edit, and invalidates the cached copy `GET` on this campaign would otherwise keep serving.
|
|
3669
3669
|
# @param campaign_id [String] Google platform campaign ID
|
|
3670
3670
|
# @param update_campaign_targeting_request [UpdateCampaignTargetingRequest]
|
|
3671
3671
|
# @param [Hash] opts the optional parameters
|
|
@@ -1028,11 +1028,11 @@ module Zernio
|
|
|
1028
1028
|
end
|
|
1029
1029
|
|
|
1030
1030
|
# Get OAuth connect URL
|
|
1031
|
-
# Initiate an OAuth connection flow. Returns an authUrl to redirect the user to. Standard flow: Zernio hosts the selection UI, then redirects to your redirect_url. Headless mode (headless=true): user is redirected to your redirect_url with OAuth data for custom UI. Use the platform-specific selection endpoints to complete.
|
|
1031
|
+
# Initiate an OAuth connection flow. Returns an authUrl to redirect the user to. Standard flow: Zernio hosts the selection UI, then redirects to your redirect_url. Headless mode (headless=true): user is redirected to your redirect_url with OAuth data for custom UI. Use the platform-specific selection endpoints to complete. TikTok: every connection now goes through the TikTok for Business app. One TikTok account per profile, so connecting on a profile that already holds one replaces it. Reconnecting the SAME account keeps it and all of its history; authorizing a DIFFERENT TikTok account takes the slot over and permanently deletes the previous account's analytics, inbox and DM history. The two are told apart by the `@handle` stored at the last connect, so an account whose handle has been renamed on TikTok since then reads as a different account. An authorization that leaves out a permission the connected account needs changes nothing at all and comes back as `missing_tiktok_permissions`; connect again and accept every permission on TikTok's screen.
|
|
1032
1032
|
# @param platform [String] Social media platform to connect. `snapchat` is a closed beta with no public release date: it returns 403 `PLATFORM_BETA_RESTRICTED` until the account is approved.
|
|
1033
1033
|
# @param profile_id [String] Your Zernio profile ID (get from /v1/profiles). For WhatsApp, a Zernio-provisioned number can only be connected on the profile it was provisioned to; connecting from any other profile is rejected with a 409.
|
|
1034
1034
|
# @param [Hash] opts the optional parameters
|
|
1035
|
-
# @option opts [String] :redirect_url Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, platform_requires_destination,
|
|
1035
|
+
# @option opts [String] :redirect_url Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, missing_tiktok_permissions, platform_requires_destination, reconnect_account_mismatch, invalid_request Access and limits: profile_not_found, invalid_profile_id, access_denied, account_limit_exceeded, profile_limit_exceeded, payment_required Destination selection: no_facebook_pages, facebook_pages_error, no_google_locations, google_locations_error, google_permission_denied, no_snapchat_public_profiles, snapchat_profiles_error, discord_no_guild, slack_no_team WhatsApp: whatsapp_error, one_whatsapp_per_profile, whatsapp_number_already_connected, whatsapp_number_pinned_to_profile, connection_cancelled Google Ads (platform=googleads): google_ads_auth_failed, google_ads_invalid_state, google_ads_config_error, google_ads_token_failed, google_ads_quota_exhausted, google_ads_callback_error TikTok Ads (platform=tiktokads): tiktok_ads_auth_failed, tiktok_ads_invalid_state, tiktok_ads_access_denied, tiktok_ads_config_error, tiktok_ads_token_failed, tiktok_ads_account_not_found, tiktok_ads_callback_error X Ads (platform=xads): x_ads_denied, x_ads_auth_failed, x_ads_config_error, x_ads_account_not_found, x_ads_state_error, x_ads_token_failed, x_ads_token_missing, x_ads_callback_error Shopify (platform=shopify): shopify_auth_failed, shopify_config_error, shopify_invalid_state, shopify_invalid_hmac, shopify_invalid_shop, shopify_missing_scopes, shopify_callback_error 1. On this endpoint every upstream OAuth error is collapsed into `oauth_denied`. The provider's own value (for example Meta's `access_denied`) is not forwarded. The dedicated ads flows below are different: they use their own denial slugs and `google_ads_auth_failed` and `tiktok_ads_auth_failed` may carry the provider's raw error string in `error_message`. 2. On the tiktok and twitter ads flows `platform` carries the ads platform id (`tiktokads`, `xads`), not the value used in the request path. The googleads and shopify flows report `googleads` and `shopify`. 3. `missing_tiktok_permissions` means the TikTok authorization left out a permission the already-connected account needs, so nothing was changed and it keeps working as before. It is user-fixable: connect again and accept every permission on TikTok's screen.
|
|
1036
1036
|
# @option opts [Boolean] :headless When true, the user is redirected to your redirect_url with raw OAuth data (code, state) instead of Zernio's default account selection UI. Use this to build a custom connect experience. (default to false)
|
|
1037
1037
|
# @option opts [String] :login_method Instagram only. Which of the two Instagram connection methods to use. Ignored for every other platform. `instagram_login` (the default, and what you get if you omit this): the Instagram Login dialog. The user authorizes their Instagram professional account directly, no Facebook Page required. `facebook_login`: the Facebook Login dialog, i.e. \"Instagram API with Facebook Login\". The user authorizes a Facebook Page that has a linked Instagram professional account, and every API call for that account then runs through the Page. Use this when the customer manages Instagram through a Page and expects the Facebook consent screen. Because the user has to pick which Page to connect, the callback continues at the account-selection step, `/v1/connect/instagram/select-account`. `facebook_login` supports `headless=true` like the other selection platforms: the callback redirects to your `redirect_url` with `profileId`, `tempToken`, `platform=instagram`, `step=select_account` and `connect_token`, which you pass into the select-account endpoints to finish. The default `instagram_login` has no selection step, so it connects the account directly. (default to 'instagram_login')
|
|
1038
1038
|
# @option opts [String] :onboarding WhatsApp only. Ignored for every other platform. Controls which screen Meta's Embedded Signup popup shows. If omitted, the connection defaults to coexistence (same as `business_app` below), preserving existing behavior for numbers already on the WhatsApp Business app. `api`: standard Embedded Signup, showing Meta's WABA/number picker. Use this to connect a phone number already on Cloud API elsewhere. `business_app`: coexistence, i.e. 'Connect existing WhatsApp Business app' (a number shared between Cloud API and the consumer WhatsApp Business app).
|
|
@@ -1047,11 +1047,11 @@ module Zernio
|
|
|
1047
1047
|
end
|
|
1048
1048
|
|
|
1049
1049
|
# Get OAuth connect URL
|
|
1050
|
-
# Initiate an OAuth connection flow. Returns an authUrl to redirect the user to. Standard flow: Zernio hosts the selection UI, then redirects to your redirect_url. Headless mode (headless=true): user is redirected to your redirect_url with OAuth data for custom UI. Use the platform-specific selection endpoints to complete.
|
|
1050
|
+
# Initiate an OAuth connection flow. Returns an authUrl to redirect the user to. Standard flow: Zernio hosts the selection UI, then redirects to your redirect_url. Headless mode (headless=true): user is redirected to your redirect_url with OAuth data for custom UI. Use the platform-specific selection endpoints to complete. TikTok: every connection now goes through the TikTok for Business app. One TikTok account per profile, so connecting on a profile that already holds one replaces it. Reconnecting the SAME account keeps it and all of its history; authorizing a DIFFERENT TikTok account takes the slot over and permanently deletes the previous account's analytics, inbox and DM history. The two are told apart by the `@handle` stored at the last connect, so an account whose handle has been renamed on TikTok since then reads as a different account. An authorization that leaves out a permission the connected account needs changes nothing at all and comes back as `missing_tiktok_permissions`; connect again and accept every permission on TikTok's screen.
|
|
1051
1051
|
# @param platform [String] Social media platform to connect. `snapchat` is a closed beta with no public release date: it returns 403 `PLATFORM_BETA_RESTRICTED` until the account is approved.
|
|
1052
1052
|
# @param profile_id [String] Your Zernio profile ID (get from /v1/profiles). For WhatsApp, a Zernio-provisioned number can only be connected on the profile it was provisioned to; connecting from any other profile is rejected with a 409.
|
|
1053
1053
|
# @param [Hash] opts the optional parameters
|
|
1054
|
-
# @option opts [String] :redirect_url Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, platform_requires_destination,
|
|
1054
|
+
# @option opts [String] :redirect_url Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, missing_tiktok_permissions, platform_requires_destination, reconnect_account_mismatch, invalid_request Access and limits: profile_not_found, invalid_profile_id, access_denied, account_limit_exceeded, profile_limit_exceeded, payment_required Destination selection: no_facebook_pages, facebook_pages_error, no_google_locations, google_locations_error, google_permission_denied, no_snapchat_public_profiles, snapchat_profiles_error, discord_no_guild, slack_no_team WhatsApp: whatsapp_error, one_whatsapp_per_profile, whatsapp_number_already_connected, whatsapp_number_pinned_to_profile, connection_cancelled Google Ads (platform=googleads): google_ads_auth_failed, google_ads_invalid_state, google_ads_config_error, google_ads_token_failed, google_ads_quota_exhausted, google_ads_callback_error TikTok Ads (platform=tiktokads): tiktok_ads_auth_failed, tiktok_ads_invalid_state, tiktok_ads_access_denied, tiktok_ads_config_error, tiktok_ads_token_failed, tiktok_ads_account_not_found, tiktok_ads_callback_error X Ads (platform=xads): x_ads_denied, x_ads_auth_failed, x_ads_config_error, x_ads_account_not_found, x_ads_state_error, x_ads_token_failed, x_ads_token_missing, x_ads_callback_error Shopify (platform=shopify): shopify_auth_failed, shopify_config_error, shopify_invalid_state, shopify_invalid_hmac, shopify_invalid_shop, shopify_missing_scopes, shopify_callback_error 1. On this endpoint every upstream OAuth error is collapsed into `oauth_denied`. The provider's own value (for example Meta's `access_denied`) is not forwarded. The dedicated ads flows below are different: they use their own denial slugs and `google_ads_auth_failed` and `tiktok_ads_auth_failed` may carry the provider's raw error string in `error_message`. 2. On the tiktok and twitter ads flows `platform` carries the ads platform id (`tiktokads`, `xads`), not the value used in the request path. The googleads and shopify flows report `googleads` and `shopify`. 3. `missing_tiktok_permissions` means the TikTok authorization left out a permission the already-connected account needs, so nothing was changed and it keeps working as before. It is user-fixable: connect again and accept every permission on TikTok's screen.
|
|
1055
1055
|
# @option opts [Boolean] :headless When true, the user is redirected to your redirect_url with raw OAuth data (code, state) instead of Zernio's default account selection UI. Use this to build a custom connect experience. (default to false)
|
|
1056
1056
|
# @option opts [String] :login_method Instagram only. Which of the two Instagram connection methods to use. Ignored for every other platform. `instagram_login` (the default, and what you get if you omit this): the Instagram Login dialog. The user authorizes their Instagram professional account directly, no Facebook Page required. `facebook_login`: the Facebook Login dialog, i.e. \"Instagram API with Facebook Login\". The user authorizes a Facebook Page that has a linked Instagram professional account, and every API call for that account then runs through the Page. Use this when the customer manages Instagram through a Page and expects the Facebook consent screen. Because the user has to pick which Page to connect, the callback continues at the account-selection step, `/v1/connect/instagram/select-account`. `facebook_login` supports `headless=true` like the other selection platforms: the callback redirects to your `redirect_url` with `profileId`, `tempToken`, `platform=instagram`, `step=select_account` and `connect_token`, which you pass into the select-account endpoints to finish. The default `instagram_login` has no selection step, so it connects the account directly. (default to 'instagram_login')
|
|
1057
1057
|
# @option opts [String] :onboarding WhatsApp only. Ignored for every other platform. Controls which screen Meta's Embedded Signup popup shows. If omitted, the connection defaults to coexistence (same as `business_app` below), preserving existing behavior for numbers already on the WhatsApp Business app. `api`: standard Embedded Signup, showing Meta's WABA/number picker. Use this to connect a phone number already on Cloud API elsewhere. `business_app`: coexistence, i.e. 'Connect existing WhatsApp Business app' (a number shared between Cloud API and the consumer WhatsApp Business app).
|
|
@@ -36,7 +36,7 @@ module Zernio
|
|
|
36
36
|
# When true, saves the post as a draft. When none of scheduledFor, publishNow, or queuedFromProfile are provided, the post defaults to draft automatically.
|
|
37
37
|
attr_accessor :is_draft
|
|
38
38
|
|
|
39
|
-
# TikTok only. Preview whether each `tiktok` entry in `platforms` could publish right now under the TikTok Direct Post daily limits, without creating, scheduling or publishing anything: no post is persisted and no upload slot is claimed, so it can be repeated freely. The request still goes through auth, the payment gate and body validation, then returns HTTP 200 with `{ dryRun: true, canPublish, tiktok: [...] }` instead of 201. Only `tiktok` entries are evaluated; other platforms in the body are ignored, and a body with no `tiktok` entry is rejected with 400 `invalid_field_value` on `platforms`. An entry with `platformSpecificData.tiktokSettings.draft: true` (Creator Inbox upload) is not subject to the limit and always reports `canPublish: true`.
|
|
39
|
+
# TikTok only. Preview whether each `tiktok` entry in `platforms` could publish right now under the TikTok Direct Post daily limits, without creating, scheduling or publishing anything: no post is persisted and no upload slot is claimed, so it can be repeated freely. The request still goes through auth, the payment gate and body validation, then returns HTTP 200 with `{ dryRun: true, canPublish, tiktok: [...] }` instead of 201. Only `tiktok` entries are evaluated; other platforms in the body are ignored, and a body with no `tiktok` entry is rejected with 400 `invalid_field_value` on `platforms`. An entry with `platformSpecificData.tiktokSettings.draft: true` (Creator Inbox upload) is not subject to the limit and always reports `canPublish: true`. Accounts connected through the TikTok for Business app do not go through these limits at all and also always report `canPublish: true`, so on those accounts a dry run confirms the request is well-formed rather than gating it.
|
|
40
40
|
attr_accessor :dry_run
|
|
41
41
|
|
|
42
42
|
# IANA timezone (`Europe/Madrid`, `America/New_York`) used to interpret a `scheduledFor` (root or per-platform) that carries no `Z` or offset. Has no effect on values that already carry one. An unknown name returns 400 when `scheduledFor` is set.
|
|
@@ -19,7 +19,7 @@ module Zernio
|
|
|
19
19
|
|
|
20
20
|
attr_accessor :status
|
|
21
21
|
|
|
22
|
-
# Why the node could not be validated (
|
|
22
|
+
# Why the node could not be validated (on skipped), or what the dry run could not check and what the request would do as sent (on validated). A Performance Max validation with no location targeting reports here that the campaign would run worldwide.
|
|
23
23
|
attr_accessor :reason
|
|
24
24
|
|
|
25
25
|
class EnumAttributeValidator
|
|
@@ -65,10 +65,10 @@ module Zernio
|
|
|
65
65
|
# Required on legacy, multi-creative and Performance Max 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.
|
|
66
66
|
attr_accessor :budget_type
|
|
67
67
|
|
|
68
|
-
# Google Performance Max accepts PAUSED only and always creates a paused campaign. Meta, TikTok, and LinkedIn: publish state of the created entities. Omitted or ACTIVE publishes live (default, back-compat); PAUSED creates them paused so you can review before they spend. On Meta the pause is held on the campaign this call creates, leaving the ad set and ad switched on, so a single PUT /v1/ads/campaigns/{campaignId}/status with `active` brings the whole thing live. It is held at every level instead when the pause cannot rely on the campaign: `existingCampaignId` (that campaign may be running and is never touched) or `campaignStatus: ACTIVE`. On TikTok the whole campaign > ad group > ad hierarchy stays paused. On LinkedIn the whole campaign group, campaign, and creative hierarchy stays PAUSED (intendedStatus PAUSED on each).
|
|
68
|
+
# Google Performance Max accepts PAUSED only and always creates a paused campaign. Google Search and Display, Meta, TikTok, and LinkedIn: publish state of the created entities. Omitted or ACTIVE publishes live (default, back-compat); PAUSED creates them paused so you can review before they spend. On Meta the pause is held on the campaign this call creates, leaving the ad set and ad switched on, so a single PUT /v1/ads/campaigns/{campaignId}/status with `active` brings the whole thing live. It is held at every level instead when the pause cannot rely on the campaign: `existingCampaignId` (that campaign may be running and is never touched) or `campaignStatus: ACTIVE`. Google Search and Display follow the same rule, and because Google keeps an independent switch at campaign, ad group and ad level, a PAUSED create leaves the campaign it creates PAUSED at Google. On TikTok the whole campaign > ad group > ad hierarchy stays paused. On LinkedIn the whole campaign group, campaign, and creative hierarchy stays PAUSED (intendedStatus PAUSED on each).
|
|
69
69
|
attr_accessor :status
|
|
70
70
|
|
|
71
|
-
# Meta
|
|
71
|
+
# Meta and Google. Overrides `status` for the campaign level alone, so you can create a live campaign whose ad set and ad stay paused, or the reverse. Omitted, it follows `status`.
|
|
72
72
|
attr_accessor :campaign_status
|
|
73
73
|
|
|
74
74
|
# Meta only. Where the budget lives, which selects the Meta budget model: - `adset` (default): ABO (Ad-set Budget Optimization). The budget is set on the ad set. This is the back-compatible behaviour; omit this field to keep it. - `campaign`: CBO (Campaign Budget Optimization / Advantage Campaign Budget). The budget AND `bidStrategy` are set on the CAMPAIGN, and Meta distributes spend across ad sets automatically. The returned ad stores the applied `budgetLevel` and budget in `campaignBudget` for CBO or `adSetBudget` for ABO. Edit CBO budgets with `PUT /v1/ads/campaigns/{campaignId}` and ABO budgets with `PUT /v1/ads/ad-sets/{adSetId}`. Meta requires the budget at exactly one level, never both. Non-Meta platforms ignore this field. Ignored on the attach shape (`adSetId`), which inherits the existing budget.
|
|
@@ -19,7 +19,7 @@ module Zernio
|
|
|
19
19
|
|
|
20
20
|
attr_accessor :included
|
|
21
21
|
|
|
22
|
-
#
|
|
22
|
+
# Google's bid adjustment for this device: null when it has none, 0 when the device is switched off, otherwise 0.1 to 10.
|
|
23
23
|
attr_accessor :bid_modifier
|
|
24
24
|
|
|
25
25
|
class EnumAttributeValidator
|
|
@@ -52,7 +52,7 @@ module Zernio
|
|
|
52
52
|
# Optional for video posts. Timestamp in milliseconds to select which frame to use as thumbnail (defaults to 1000ms/1 second). Ignored when videoCoverImageUrl is provided.
|
|
53
53
|
attr_accessor :video_cover_timestamp_ms
|
|
54
54
|
|
|
55
|
-
# Optional for video posts. URL of a custom thumbnail image (JPG, PNG, or WebP, max 20MB). The image is stitched as a single frame at the start of the video
|
|
55
|
+
# Optional for video posts. URL of a custom thumbnail image (JPG, PNG, or WebP, max 20MB). Any downloadable URL works: we rehost it ourselves. The image is stitched as a single frame at the start of the video to serve as the cover. Accounts connected through the TikTok for Business app hand it to TikTok as the cover instead, with no stitching, falling back to videoCoverTimestampMs without it. Overrides videoCoverTimestampMs when provided.
|
|
56
56
|
attr_accessor :video_cover_image_url
|
|
57
57
|
|
|
58
58
|
# Optional for photo carousels. Index of image to use as cover, 0-based (defaults to 0/first image).
|
|
@@ -22,7 +22,7 @@ module Zernio
|
|
|
22
22
|
# Google only. The FULL desired set of negative keywords for the entire ad group, independent of positives. Omit to leave negatives unchanged; [] removes all negatives. Uses the same text/match-type identity and preservation contract as keywords above. Strings and objects without matchType default to BROAD, so resending an EXACT or PHRASE negative as a bare string requests a different criterion. Campaign negatives are separate: use /v1/ads/campaigns/{campaignId}/negative-keywords to manage those.
|
|
23
23
|
attr_accessor :negative_keywords
|
|
24
24
|
|
|
25
|
-
# Google only. The FULL new set of device
|
|
25
|
+
# Google only. The FULL new set of device bid modifiers for the campaign. Entries are a device name alone (targeted, bid modifier reset to 1) or { device, bidModifier }. A supported device you leave out is switched off, written as a bid modifier of 0. Google never removes a device criterion, so an excluded device reads back as bidModifier 0 rather than disappearing, and a set that switches every device off returns 422. Which devices a campaign carries depends on its channel: Search campaigns have MOBILE, DESKTOP and TABLET, Display campaigns also have CONNECTED_TV, and sending a device the campaign does not carry returns 422.
|
|
26
26
|
attr_accessor :devices
|
|
27
27
|
|
|
28
28
|
attr_accessor :age_min
|
|
@@ -17,7 +17,7 @@ module Zernio
|
|
|
17
17
|
class UpdateAdRequestTargetingDevicesInnerOneOf < ApiModelBase
|
|
18
18
|
attr_accessor :device
|
|
19
19
|
|
|
20
|
-
# Google device bid modifier
|
|
20
|
+
# Google device bid modifier. 0 switches the device off (minus 100%); otherwise 0.1 to 10 (minus 90% to plus 900%). Google rejects any value between 0 and 0.1.
|
|
21
21
|
attr_accessor :bid_modifier
|
|
22
22
|
|
|
23
23
|
class EnumAttributeValidator
|
|
@@ -114,8 +114,8 @@ module Zernio
|
|
|
114
114
|
invalid_properties.push('invalid value for "bid_modifier", must be smaller than or equal to 10.')
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
-
if !@bid_modifier.nil? && @bid_modifier < 0
|
|
118
|
-
invalid_properties.push('invalid value for "bid_modifier", must be greater than or equal to 0.
|
|
117
|
+
if !@bid_modifier.nil? && @bid_modifier < 0
|
|
118
|
+
invalid_properties.push('invalid value for "bid_modifier", must be greater than or equal to 0.')
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
invalid_properties
|
|
@@ -129,7 +129,7 @@ module Zernio
|
|
|
129
129
|
device_validator = EnumAttributeValidator.new('String', ["MOBILE", "DESKTOP", "TABLET", "CONNECTED_TV"])
|
|
130
130
|
return false unless device_validator.valid?(@device)
|
|
131
131
|
return false if !@bid_modifier.nil? && @bid_modifier > 10
|
|
132
|
-
return false if !@bid_modifier.nil? && @bid_modifier < 0
|
|
132
|
+
return false if !@bid_modifier.nil? && @bid_modifier < 0
|
|
133
133
|
true
|
|
134
134
|
end
|
|
135
135
|
|
|
@@ -154,8 +154,8 @@ module Zernio
|
|
|
154
154
|
fail ArgumentError, 'invalid value for "bid_modifier", must be smaller than or equal to 10.'
|
|
155
155
|
end
|
|
156
156
|
|
|
157
|
-
if bid_modifier < 0
|
|
158
|
-
fail ArgumentError, 'invalid value for "bid_modifier", must be greater than or equal to 0.
|
|
157
|
+
if bid_modifier < 0
|
|
158
|
+
fail ArgumentError, 'invalid value for "bid_modifier", must be greater than or equal to 0.'
|
|
159
159
|
end
|
|
160
160
|
|
|
161
161
|
@bid_modifier = bid_modifier
|
data/lib/zernio-sdk/version.rb
CHANGED
data/openapi.yaml
CHANGED
|
@@ -113,7 +113,7 @@ x-documentation:
|
|
|
113
113
|
| Instagram | Yes | Yes | Yes | Yes |
|
|
114
114
|
| Facebook | Yes | Stories | Yes | Yes |
|
|
115
115
|
| LinkedIn | Yes | - | Partial | - |
|
|
116
|
-
| TikTok | Yes | - | Yes |
|
|
116
|
+
| TikTok | Yes | - | Yes | Comments |
|
|
117
117
|
| YouTube | Yes | Shorts | Yes | Yes |
|
|
118
118
|
| Pinterest | Yes | - | Yes | - |
|
|
119
119
|
| Reddit | Yes | - | Partial | Yes |
|
|
@@ -129,6 +129,8 @@ x-documentation:
|
|
|
129
129
|
|
|
130
130
|
> **Google Business Profile Analytics Note:** Per-post analytics for Google Business Profile are deprecated by Google with no replacement, so Google Business Profile posts always report `syncStatus: "unavailable"` with an explanatory `errorMessage`. Location-level metrics (impressions, clicks, calls, directions, bookings) are available via the dedicated `/v1/analytics/googlebusiness/performance` endpoint.
|
|
131
131
|
|
|
132
|
+
> **TikTok Inbox Note:** Comment management (read, reply, hide, pin, like, delete) needs an account connected through the TikTok for Business app, which is what every new TikTok connection now uses. It does not require the TikTok account itself to be a Business Account. An account connected earlier through the TikTok developer app returns 400 `PLATFORM_LIMITATION` on the comment routes until it is reconnected. TikTok direct messages are not generally available: on top of the same connection they need a real TikTok Business Account whose sign-up region is outside the EEA, Switzerland and the UK, plus TikTok messaging approval for the calling app, and TikTok reports every one of those as a misleading "access token is invalid". Do not treat that as an expired token.
|
|
133
|
+
|
|
132
134
|
## Rate Limits
|
|
133
135
|
|
|
134
136
|
API request throughput is rate-limited per minute on a sliding window. Limits scale with your team's total connected accounts:
|
|
@@ -141,6 +143,8 @@ x-documentation:
|
|
|
141
143
|
|
|
142
144
|
Posts themselves are unlimited on every connected account; the rate limit applies only to API request throughput. Zernio also applies per-account daily publish caps as an anti-abuse safeguard (Instagram 100/day, Facebook 100/day, Threads 250/day, X 50/day, Pinterest 25/day, 50/day for every other platform), plus a 25-posts-per-hour per-account velocity cap.
|
|
143
145
|
|
|
146
|
+
TikTok caps accounts connected through the TikTok for Business app lower than any of those: 15 videos and 15 photo posts per account per day, counted separately. Zernio enforces it as a trailing 24 hour window rather than a calendar day, and a post over the cap is not rejected. It is held and published automatically once the oldest post of the same kind leaves the window, so `scheduledFor` on that platform entry moves to the time it will go out.
|
|
147
|
+
|
|
144
148
|
All responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers. Read these instead of hard-coding limits, since your tier may be configured higher than the default.
|
|
145
149
|
|
|
146
150
|
## Webhooks
|
|
@@ -6813,7 +6817,7 @@ components:
|
|
|
6813
6817
|
videoCoverImageUrl:
|
|
6814
6818
|
type: string
|
|
6815
6819
|
format: uri
|
|
6816
|
-
description: 'Optional for video posts. URL of a custom thumbnail image (JPG, PNG, or WebP, max 20MB). The image is stitched as a single frame at the start of the video
|
|
6820
|
+
description: 'Optional for video posts. URL of a custom thumbnail image (JPG, PNG, or WebP, max 20MB). Any downloadable URL works: we rehost it ourselves. The image is stitched as a single frame at the start of the video to serve as the cover. Accounts connected through the TikTok for Business app hand it to TikTok as the cover instead, with no stitching, falling back to videoCoverTimestampMs without it. Overrides videoCoverTimestampMs when provided.'
|
|
6817
6821
|
photoCoverIndex:
|
|
6818
6822
|
type: integer
|
|
6819
6823
|
description: Optional for photo carousels. Index of image to use as cover, 0-based (defaults to 0/first image).
|
|
@@ -16370,7 +16374,7 @@ paths:
|
|
|
16370
16374
|
dryRun:
|
|
16371
16375
|
type: boolean
|
|
16372
16376
|
default: false
|
|
16373
|
-
description: 'TikTok only. Preview whether each `tiktok` entry in `platforms` could publish right now under the TikTok Direct Post daily limits, without creating, scheduling or publishing anything: no post is persisted and no upload slot is claimed, so it can be repeated freely. The request still goes through auth, the payment gate and body validation, then returns HTTP 200 with `{ dryRun: true, canPublish, tiktok: [...] }` instead of 201. Only `tiktok` entries are evaluated; other platforms in the body are ignored, and a body with no `tiktok` entry is rejected with 400 `invalid_field_value` on `platforms`. An entry with `platformSpecificData.tiktokSettings.draft: true` (Creator Inbox upload) is not subject to the limit and always reports `canPublish: true`.'
|
|
16377
|
+
description: 'TikTok only. Preview whether each `tiktok` entry in `platforms` could publish right now under the TikTok Direct Post daily limits, without creating, scheduling or publishing anything: no post is persisted and no upload slot is claimed, so it can be repeated freely. The request still goes through auth, the payment gate and body validation, then returns HTTP 200 with `{ dryRun: true, canPublish, tiktok: [...] }` instead of 201. Only `tiktok` entries are evaluated; other platforms in the body are ignored, and a body with no `tiktok` entry is rejected with 400 `invalid_field_value` on `platforms`. An entry with `platformSpecificData.tiktokSettings.draft: true` (Creator Inbox upload) is not subject to the limit and always reports `canPublish: true`. Accounts connected through the TikTok for Business app do not go through these limits at all and also always report `canPublish: true`, so on those accounts a dry run confirms the request is well-formed rather than gating it.'
|
|
16374
16378
|
timezone:
|
|
16375
16379
|
type: string
|
|
16376
16380
|
default: UTC
|
|
@@ -19445,6 +19449,15 @@ paths:
|
|
|
19445
19449
|
description: |
|
|
19446
19450
|
Initiate an OAuth connection flow. Returns an authUrl to redirect the user to.
|
|
19447
19451
|
Standard flow: Zernio hosts the selection UI, then redirects to your redirect_url. Headless mode (headless=true): user is redirected to your redirect_url with OAuth data for custom UI. Use the platform-specific selection endpoints to complete.
|
|
19452
|
+
|
|
19453
|
+
TikTok: every connection now goes through the TikTok for Business app. One TikTok account per
|
|
19454
|
+
profile, so connecting on a profile that already holds one replaces it. Reconnecting the SAME
|
|
19455
|
+
account keeps it and all of its history; authorizing a DIFFERENT TikTok account takes the slot
|
|
19456
|
+
over and permanently deletes the previous account's analytics, inbox and DM history. The two
|
|
19457
|
+
are told apart by the `@handle` stored at the last connect, so an account whose handle has
|
|
19458
|
+
been renamed on TikTok since then reads as a different account. An authorization that leaves
|
|
19459
|
+
out a permission the connected account needs changes nothing at all and comes back as
|
|
19460
|
+
`missing_tiktok_permissions`; connect again and accept every permission on TikTok's screen.
|
|
19448
19461
|
parameters:
|
|
19449
19462
|
- name: platform
|
|
19450
19463
|
in: path
|
|
@@ -19475,8 +19488,8 @@ paths:
|
|
|
19475
19488
|
OAuth and callback:
|
|
19476
19489
|
oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed,
|
|
19477
19490
|
internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported,
|
|
19478
|
-
missing_google_permissions,
|
|
19479
|
-
invalid_request
|
|
19491
|
+
missing_google_permissions, missing_tiktok_permissions, platform_requires_destination,
|
|
19492
|
+
reconnect_account_mismatch, invalid_request
|
|
19480
19493
|
|
|
19481
19494
|
Access and limits:
|
|
19482
19495
|
profile_not_found, invalid_profile_id, access_denied, account_limit_exceeded,
|
|
@@ -19516,6 +19529,10 @@ paths:
|
|
|
19516
19529
|
2. On the tiktok and twitter ads flows `platform` carries the ads platform id
|
|
19517
19530
|
(`tiktokads`, `xads`), not the value used in the request path. The googleads and shopify
|
|
19518
19531
|
flows report `googleads` and `shopify`.
|
|
19532
|
+
|
|
19533
|
+
3. `missing_tiktok_permissions` means the TikTok authorization left out a permission the
|
|
19534
|
+
already-connected account needs, so nothing was changed and it keeps working as before.
|
|
19535
|
+
It is user-fixable: connect again and accept every permission on TikTok's screen.
|
|
19519
19536
|
- name: headless
|
|
19520
19537
|
in: query
|
|
19521
19538
|
schema: { type: boolean, default: false }
|
|
@@ -43988,6 +44005,12 @@ paths:
|
|
|
43988
44005
|
|
|
43989
44006
|
On Meta this flips the campaign only. An ad set paused in its own right stays paused, so pair this with
|
|
43990
44007
|
PUT /v1/ads/ad-sets/{adSetId}/status when you also need the ad set switched back on.
|
|
44008
|
+
|
|
44009
|
+
Google keeps an independent on/off switch at campaign, ad group and ad level and the most restrictive
|
|
44010
|
+
one wins, so `active` switches the campaign on TOGETHER with the ad groups and ads Zernio tracks under
|
|
44011
|
+
it, in one mutate. Without that the campaign reads ENABLED while a paused ad group or ad keeps it from
|
|
44012
|
+
serving. `paused` writes the campaign alone, which already stops delivery and leaves each ad's own
|
|
44013
|
+
switch as you set it.
|
|
43991
44014
|
security:
|
|
43992
44015
|
- bearerAuth: []
|
|
43993
44016
|
parameters:
|
|
@@ -44498,10 +44521,11 @@ paths:
|
|
|
44498
44521
|
(10 minutes fresh, up to 7 days last-good), not always a live read. Google
|
|
44499
44522
|
only; every other platform returns 501.
|
|
44500
44523
|
|
|
44501
|
-
`devices`
|
|
44502
|
-
|
|
44503
|
-
|
|
44504
|
-
|
|
44524
|
+
`devices` lists the device criteria the campaign carries, which depends on
|
|
44525
|
+
its channel: Search campaigns have MOBILE, DESKTOP and TABLET, Display
|
|
44526
|
+
campaigns also have CONNECTED_TV. `bidModifier` is Google's bid adjustment
|
|
44527
|
+
for that device, `null` when it has none, and `0` when the device is
|
|
44528
|
+
switched off; `included` is false for exactly that case.
|
|
44505
44529
|
security:
|
|
44506
44530
|
- bearerAuth: []
|
|
44507
44531
|
parameters:
|
|
@@ -44522,7 +44546,7 @@ paths:
|
|
|
44522
44546
|
properties:
|
|
44523
44547
|
device: { type: string, enum: [MOBILE, DESKTOP, TABLET, CONNECTED_TV] }
|
|
44524
44548
|
included: { type: boolean }
|
|
44525
|
-
bidModifier: { type: [number, "null"], description: "
|
|
44549
|
+
bidModifier: { type: [number, "null"], description: "Google's bid adjustment for this device: null when it has none, 0 when the device is switched off, otherwise 0.1 to 10." }
|
|
44526
44550
|
locations:
|
|
44527
44551
|
type: array
|
|
44528
44552
|
items:
|
|
@@ -44564,6 +44588,11 @@ paths:
|
|
|
44564
44588
|
out of the body are untouched. Google only; every other platform returns
|
|
44565
44589
|
501.
|
|
44566
44590
|
|
|
44591
|
+
`devices` is the full set of device bid modifiers: a supported device you
|
|
44592
|
+
leave out is switched off with a bid modifier of 0, since Google cannot
|
|
44593
|
+
remove a device criterion. A device the campaign's channel does not carry,
|
|
44594
|
+
and a set that switches every device off, both return 422.
|
|
44595
|
+
|
|
44567
44596
|
`locations` accepts the same shapes as campaign creation: a bare array of
|
|
44568
44597
|
ISO country codes, or an object with `countries`/`regions`/`cities`/`zips`/`metros`
|
|
44569
44598
|
key lists (`key` from GET /v1/ads/targeting/search?dimension=geo). Negative
|
|
@@ -45423,7 +45452,9 @@ paths:
|
|
|
45423
45452
|
`targeting.countries` / `regions` / `cities` / `zips` / `metros`), and LANGUAGE
|
|
45424
45453
|
edits via `targeting.languages`.
|
|
45425
45454
|
Each list you send becomes the FULL new set of its kind (criteria not in the
|
|
45426
|
-
list are removed
|
|
45455
|
+
list are removed, except devices, which Google cannot remove and which are
|
|
45456
|
+
switched off with a bid modifier of 0 instead); a kind left out is untouched.
|
|
45457
|
+
Any other `targeting` field
|
|
45427
45458
|
returns 400: Google cannot mutate it post-create without recreating
|
|
45428
45459
|
the campaign. Creative edits are dispatched on the ad's `advertisingChannelType`,
|
|
45429
45460
|
and every supported field replaces a whole set; a field you omit is preserved.
|
|
@@ -45604,7 +45635,7 @@ paths:
|
|
|
45604
45635
|
- { type: object, required: [text], properties: { text: { type: string, maxLength: 80 }, matchType: { type: string, enum: [exact, phrase, broad] } } }
|
|
45605
45636
|
devices:
|
|
45606
45637
|
type: array
|
|
45607
|
-
description: "Google only. The FULL new set of device
|
|
45638
|
+
description: "Google only. The FULL new set of device bid modifiers for the campaign. Entries are a device name alone (targeted, bid modifier reset to 1) or { device, bidModifier }. A supported device you leave out is switched off, written as a bid modifier of 0. Google never removes a device criterion, so an excluded device reads back as bidModifier 0 rather than disappearing, and a set that switches every device off returns 422. Which devices a campaign carries depends on its channel: Search campaigns have MOBILE, DESKTOP and TABLET, Display campaigns also have CONNECTED_TV, and sending a device the campaign does not carry returns 422."
|
|
45608
45639
|
items:
|
|
45609
45640
|
oneOf:
|
|
45610
45641
|
- { type: string, enum: [MOBILE, DESKTOP, TABLET, CONNECTED_TV] }
|
|
@@ -45612,7 +45643,7 @@ paths:
|
|
|
45612
45643
|
required: [device]
|
|
45613
45644
|
properties:
|
|
45614
45645
|
device: { type: string, enum: [MOBILE, DESKTOP, TABLET, CONNECTED_TV] }
|
|
45615
|
-
bidModifier: { type: number, minimum: 0
|
|
45646
|
+
bidModifier: { type: number, minimum: 0, maximum: 10, description: 'Google device bid modifier. 0 switches the device off (minus 100%); otherwise 0.1 to 10 (minus 90% to plus 900%). Google rejects any value between 0 and 0.1.' }
|
|
45616
45647
|
ageMin: { type: integer, minimum: 13, maximum: 65 }
|
|
45617
45648
|
ageMax: { type: integer, minimum: 13, maximum: 65 }
|
|
45618
45649
|
countries: { type: array, items: { type: string }, description: "ISO 3166-1 alpha-2 codes. On Google this is the FULL new country set for the campaign (same contract as `locations`); on LinkedIn it replaces the campaign's geo criteria." }
|
|
@@ -51248,11 +51279,11 @@ paths:
|
|
|
51248
51279
|
status:
|
|
51249
51280
|
type: string
|
|
51250
51281
|
enum: [ACTIVE, PAUSED]
|
|
51251
|
-
description: "Google Performance Max accepts PAUSED only and always creates a paused campaign. Meta, TikTok, and LinkedIn: publish state of the created entities. Omitted or ACTIVE publishes live (default, back-compat); PAUSED creates them paused so you can review before they spend. On Meta the pause is held on the campaign this call creates, leaving the ad set and ad switched on, so a single PUT /v1/ads/campaigns/{campaignId}/status with `active` brings the whole thing live. It is held at every level instead when the pause cannot rely on the campaign: `existingCampaignId` (that campaign may be running and is never touched) or `campaignStatus: ACTIVE`. On TikTok the whole campaign > ad group > ad hierarchy stays paused. On LinkedIn the whole campaign group, campaign, and creative hierarchy stays PAUSED (intendedStatus PAUSED on each)."
|
|
51282
|
+
description: "Google Performance Max accepts PAUSED only and always creates a paused campaign. Google Search and Display, Meta, TikTok, and LinkedIn: publish state of the created entities. Omitted or ACTIVE publishes live (default, back-compat); PAUSED creates them paused so you can review before they spend. On Meta the pause is held on the campaign this call creates, leaving the ad set and ad switched on, so a single PUT /v1/ads/campaigns/{campaignId}/status with `active` brings the whole thing live. It is held at every level instead when the pause cannot rely on the campaign: `existingCampaignId` (that campaign may be running and is never touched) or `campaignStatus: ACTIVE`. Google Search and Display follow the same rule, and because Google keeps an independent switch at campaign, ad group and ad level, a PAUSED create leaves the campaign it creates PAUSED at Google. On TikTok the whole campaign > ad group > ad hierarchy stays paused. On LinkedIn the whole campaign group, campaign, and creative hierarchy stays PAUSED (intendedStatus PAUSED on each)."
|
|
51252
51283
|
campaignStatus:
|
|
51253
51284
|
type: string
|
|
51254
51285
|
enum: [ACTIVE, PAUSED]
|
|
51255
|
-
description: "Meta
|
|
51286
|
+
description: "Meta and Google. Overrides `status` for the campaign level alone, so you can create a live campaign whose ad set and ad stay paused, or the reverse. Omitted, it follows `status`."
|
|
51256
51287
|
budgetLevel:
|
|
51257
51288
|
type: string
|
|
51258
51289
|
enum: [adset, campaign]
|
|
@@ -52276,7 +52307,7 @@ paths:
|
|
|
52276
52307
|
properties:
|
|
52277
52308
|
node: { type: string, enum: [campaign, adSet, creative, ad, performanceMaxCampaign] }
|
|
52278
52309
|
status: { type: string, enum: [validated, skipped] }
|
|
52279
|
-
reason: { type: string, description: "Why the node could not be validated (
|
|
52310
|
+
reason: { type: string, description: "Why the node could not be validated (on skipped), or what the dry run could not check and what the request would do as sent (on validated). A Performance Max validation with no location targeting reports here that the campaign would run worldwide." }
|
|
52280
52311
|
message: { type: string }
|
|
52281
52312
|
'201':
|
|
52282
52313
|
description: Ad(s) created
|
|
@@ -312,7 +312,7 @@ describe 'AdCampaignsApi' do
|
|
|
312
312
|
|
|
313
313
|
# unit tests for get_campaign_targeting
|
|
314
314
|
# Read a Google campaign's device, location, and language targeting
|
|
315
|
-
# Google Ads compliance requires geo, language, budget, and bidding targeting set at creation to stay editable afterwards; this reads the campaign state so an integrator can build an editor around it. Cached for the quota window (10 minutes fresh, up to 7 days last-good), not always a live read. Google only; every other platform returns 501. `devices`
|
|
315
|
+
# Google Ads compliance requires geo, language, budget, and bidding targeting set at creation to stay editable afterwards; this reads the campaign state so an integrator can build an editor around it. Cached for the quota window (10 minutes fresh, up to 7 days last-good), not always a live read. Google only; every other platform returns 501. `devices` lists the device criteria the campaign carries, which depends on its channel: Search campaigns have MOBILE, DESKTOP and TABLET, Display campaigns also have CONNECTED_TV. `bidModifier` is Google's bid adjustment for that device, `null` when it has none, and `0` when the device is switched off; `included` is false for exactly that case.
|
|
316
316
|
# @param campaign_id Google platform campaign ID
|
|
317
317
|
# @param [Hash] opts the optional parameters
|
|
318
318
|
# @option opts [String] :platform Disambiguates when the same campaignId string exists on more than one connected platform.
|
|
@@ -558,7 +558,7 @@ describe 'AdCampaignsApi' do
|
|
|
558
558
|
|
|
559
559
|
# unit tests for update_ad
|
|
560
560
|
# Update ad
|
|
561
|
-
# Patch one or more fields on an ad. Status, budget, targeting, and creative changes are propagated to the platform. Per-platform support: - **Meta** (Facebook + Instagram): all fields supported. - **TikTok**: status, budget, targeting (via `/v2/adgroup/update/`), and creative (via `/v2/ad/update/` patch-style: `headline` is ignored, `body` becomes `ad_text`). - **Google**: status, budget, KEYWORD edits via `targeting.keywords` / `targeting.negativeKeywords`, DEVICE bid adjustments via `targeting.devices`, LOCATION edits via `targeting.locations` (or the equivalent top-level `targeting.countries` / `regions` / `cities` / `zips` / `metros`), and LANGUAGE edits via `targeting.languages`. Each list you send becomes the FULL new set of its kind (criteria not in the list are removed); a kind left out is untouched.
|
|
561
|
+
# Patch one or more fields on an ad. Status, budget, targeting, and creative changes are propagated to the platform. Per-platform support: - **Meta** (Facebook + Instagram): all fields supported. - **TikTok**: status, budget, targeting (via `/v2/adgroup/update/`), and creative (via `/v2/ad/update/` patch-style: `headline` is ignored, `body` becomes `ad_text`). - **Google**: status, budget, KEYWORD edits via `targeting.keywords` / `targeting.negativeKeywords`, DEVICE bid adjustments via `targeting.devices`, LOCATION edits via `targeting.locations` (or the equivalent top-level `targeting.countries` / `regions` / `cities` / `zips` / `metros`), and LANGUAGE edits via `targeting.languages`. Each list you send becomes the FULL new set of its kind (criteria not in the list are removed, except devices, which Google cannot remove and which are switched off with a bid modifier of 0 instead); a kind left out is untouched. Any other `targeting` field returns 400: Google cannot mutate it post-create without recreating the campaign. Creative edits are dispatched on the ad's `advertisingChannelType`, and every supported field replaces a whole set; a field you omit is preserved. - **Search**: top-level `headlines`, `descriptions` and `finalUrls`. Use 3-15 headlines (1-30 characters) and 2-4 descriptions (1-90 characters). Omit an asset to remove it; omit pinnedField on an included asset to unpin it. Updates do not pad or truncate text. The legacy creative fields remain unsupported. - **Display**: top-level `headlines` (1-5, no pinnedField, display ads have no pinned positions), `descriptions` (1-5) and `finalUrls`, plus `creative.longHeadline`, `creative.businessName`, `creative.imageUrl` (the landscape marketing image) and `creative.squareImageUrl`. Each image URL is uploaded as a new Google asset and the ad is pointed at it; Google assets are immutable, so the previous asset stays in the account's asset library. - **Performance Max**: top-level `assetGroup`, which swaps asset roles on the ad's asset group. The other creative fields return 422 for this channel, and `assetGroup` returns 422 on any other channel. - **LinkedIn**: status, budget, targeting (countries or regions, excludedLocations (countries), the B2B facets, and audience segments; applied to the LinkedIn Campaign via PARTIAL_UPDATE, and REPLACES the campaign's entire targetingCriteria, not a merge), and creative (uploads new media, creates a replacement inline creative on the same campaign, pauses the old one). - **Pinterest / X / OpenAI Ads**: status + budget only. Sending `targeting` or `creative` returns 501 with code `unsupported_platform_operation`. OpenAI Ads budget is lifetime-only (see `budget.type` below). **Google location and language replacement:** locations, languages and devices are campaign-level criteria on Google, so these edits apply to every ad group and ad in the ad's campaign. Send the complete list you want to keep. Zernio diffs it against the campaign's live criteria and sends the removes and the creates in ONE `googleAds:mutate`, so the campaign is never left with a half-applied set; criteria already in the list keep their criterion ID and history. Excluded (negative) locations are left untouched. Two cases are refused rather than applied: an empty location list returns 400 (a Google campaign with no location criteria targets every country, which is never what \"remove my locations\" means, so omit the field instead), and radius targeting (`customLocations`) returns 422 because it is a separate Google criterion type that this replacement neither creates nor removes. Send either `targeting.locations` or the top-level geo fields, not both: mixing them returns 400. **Google keyword replacement:** These edits affect the ad's entire ad group, including sibling ads. Positive (`targeting.keywords`) and negative (`targeting.negativeKeywords`) sets are independent: omit a field to leave that set unchanged, or send `[]` to remove every keyword of that kind. Zernio compares each supplied set with Google's live criteria by case-insensitive keyword text and match type. A matching criterion is left untouched, retaining its criterion ID, enabled/paused status, keyword-level bid overrides, labels, and criterion-associated history/statistics. Zernio does not reset its quality score; Google continues to calculate scores and statistics normally. Text comparison does not trim whitespace. A bare string or an object without `matchType` means `broad`, not the existing criterion's match type. For example, resending an existing `{ \"text\": \"plumber\", \"matchType\": \"exact\" }` preserves it; sending `\"plumber\"` instead removes that EXACT criterion and requests a BROAD one. Changing text or match type removes criteria no longer requested and creates any missing criteria. New criteria get new IDs and do not inherit removed criteria's bid overrides, labels, or history. Historical reporting for a removed criterion is not transferred to its replacement. To add keywords without replacing a set, use [POST /v1/ads/keywords](https://docs.zernio.com/ad-campaigns/add-ad-keywords). Use `PATCH /v1/ads/keywords/{keywordId}` to pause/enable one keyword, or `DELETE /v1/ads/keywords/{keywordId}` to remove it.
|
|
562
562
|
# @param ad_id
|
|
563
563
|
# @param update_ad_request
|
|
564
564
|
# @param [Hash] opts the optional parameters
|
|
@@ -584,7 +584,7 @@ describe 'AdCampaignsApi' do
|
|
|
584
584
|
|
|
585
585
|
# unit tests for update_ad_campaign_status
|
|
586
586
|
# Pause or resume a campaign
|
|
587
|
-
# Writes the campaign's own on/off switch, then lets the platform cascade delivery to its ad sets and ads. Makes one platform API call, not one per ad. The switch is always written, whatever delivery status the ads underneath report: an ad still in review does not block resuming its campaign. The echoed `status` is the confirmation that it landed. `updated` / `skipped` describe only the ads whose own stored status CHANGED alongside it, so `updated: 0` is a normal successful response, not a no-op. Ads are skipped when they are in a terminal status (rejected, completed, cancelled), already in the target state, or switched on but not yet delivering. The last group keeps its `pending_review` / `error` status until the platform reports what it became. `skippedReasons` names which case applies. On Meta this flips the campaign only. An ad set paused in its own right stays paused, so pair this with PUT /v1/ads/ad-sets/{adSetId}/status when you also need the ad set switched back on.
|
|
587
|
+
# Writes the campaign's own on/off switch, then lets the platform cascade delivery to its ad sets and ads. Makes one platform API call, not one per ad. The switch is always written, whatever delivery status the ads underneath report: an ad still in review does not block resuming its campaign. The echoed `status` is the confirmation that it landed. `updated` / `skipped` describe only the ads whose own stored status CHANGED alongside it, so `updated: 0` is a normal successful response, not a no-op. Ads are skipped when they are in a terminal status (rejected, completed, cancelled), already in the target state, or switched on but not yet delivering. The last group keeps its `pending_review` / `error` status until the platform reports what it became. `skippedReasons` names which case applies. On Meta this flips the campaign only. An ad set paused in its own right stays paused, so pair this with PUT /v1/ads/ad-sets/{adSetId}/status when you also need the ad set switched back on. Google keeps an independent on/off switch at campaign, ad group and ad level and the most restrictive one wins, so `active` switches the campaign on TOGETHER with the ad groups and ads Zernio tracks under it, in one mutate. Without that the campaign reads ENABLED while a paused ad group or ad keeps it from serving. `paused` writes the campaign alone, which already stops delivery and leaves each ad's own switch as you set it.
|
|
588
588
|
# @param campaign_id Platform campaign ID
|
|
589
589
|
# @param update_ad_campaign_status_request
|
|
590
590
|
# @param [Hash] opts the optional parameters
|
|
@@ -688,7 +688,7 @@ describe 'AdCampaignsApi' do
|
|
|
688
688
|
|
|
689
689
|
# unit tests for update_campaign_targeting
|
|
690
690
|
# Edit a Google campaign's device, location, or language targeting
|
|
691
|
-
# Google Ads compliance row M.10: geo and language targeting set at creation must stay editable afterwards. Send at least one of `devices`, `locations`, `languages`; each provided field REPLACES that field's existing criteria on the campaign (a full set, not a delta). Fields left out of the body are untouched. Google only; every other platform returns 501. `locations` accepts the same shapes as campaign creation: a bare array of ISO country codes, or an object with `countries`/`regions`/`cities`/`zips`/`metros` key lists (`key` from GET /v1/ads/targeting/search?dimension=geo). Negative (excluded) locations are left untouched by this endpoint. An empty location list returns 400 instead of removing every criterion: a Google campaign with no location criteria targets every country, so omit `locations` to leave targeting alone. The removes and the creates go out in ONE Google `googleAds:mutate`, so a failed edit leaves the campaign's previous set intact rather than a half-applied one. `languages` is an array of Google's language codes (ISO 639-1, plus variants such as `zh_CN`); an unknown code returns 400. The response includes the refreshed `devices`/`locations`/`languages` state read back from Google after the edit, and invalidates the cached copy `GET` on this campaign would otherwise keep serving.
|
|
691
|
+
# Google Ads compliance row M.10: geo and language targeting set at creation must stay editable afterwards. Send at least one of `devices`, `locations`, `languages`; each provided field REPLACES that field's existing criteria on the campaign (a full set, not a delta). Fields left out of the body are untouched. Google only; every other platform returns 501. `devices` is the full set of device bid modifiers: a supported device you leave out is switched off with a bid modifier of 0, since Google cannot remove a device criterion. A device the campaign's channel does not carry, and a set that switches every device off, both return 422. `locations` accepts the same shapes as campaign creation: a bare array of ISO country codes, or an object with `countries`/`regions`/`cities`/`zips`/`metros` key lists (`key` from GET /v1/ads/targeting/search?dimension=geo). Negative (excluded) locations are left untouched by this endpoint. An empty location list returns 400 instead of removing every criterion: a Google campaign with no location criteria targets every country, so omit `locations` to leave targeting alone. The removes and the creates go out in ONE Google `googleAds:mutate`, so a failed edit leaves the campaign's previous set intact rather than a half-applied one. `languages` is an array of Google's language codes (ISO 639-1, plus variants such as `zh_CN`); an unknown code returns 400. The response includes the refreshed `devices`/`locations`/`languages` state read back from Google after the edit, and invalidates the cached copy `GET` on this campaign would otherwise keep serving.
|
|
692
692
|
# @param campaign_id Google platform campaign ID
|
|
693
693
|
# @param update_campaign_targeting_request
|
|
694
694
|
# @param [Hash] opts the optional parameters
|
|
@@ -217,11 +217,11 @@ describe 'ConnectApi' do
|
|
|
217
217
|
|
|
218
218
|
# unit tests for get_connect_url
|
|
219
219
|
# Get OAuth connect URL
|
|
220
|
-
# Initiate an OAuth connection flow. Returns an authUrl to redirect the user to. Standard flow: Zernio hosts the selection UI, then redirects to your redirect_url. Headless mode (headless=true): user is redirected to your redirect_url with OAuth data for custom UI. Use the platform-specific selection endpoints to complete.
|
|
220
|
+
# Initiate an OAuth connection flow. Returns an authUrl to redirect the user to. Standard flow: Zernio hosts the selection UI, then redirects to your redirect_url. Headless mode (headless=true): user is redirected to your redirect_url with OAuth data for custom UI. Use the platform-specific selection endpoints to complete. TikTok: every connection now goes through the TikTok for Business app. One TikTok account per profile, so connecting on a profile that already holds one replaces it. Reconnecting the SAME account keeps it and all of its history; authorizing a DIFFERENT TikTok account takes the slot over and permanently deletes the previous account's analytics, inbox and DM history. The two are told apart by the `@handle` stored at the last connect, so an account whose handle has been renamed on TikTok since then reads as a different account. An authorization that leaves out a permission the connected account needs changes nothing at all and comes back as `missing_tiktok_permissions`; connect again and accept every permission on TikTok's screen.
|
|
221
221
|
# @param platform Social media platform to connect. `snapchat` is a closed beta with no public release date: it returns 403 `PLATFORM_BETA_RESTRICTED` until the account is approved.
|
|
222
222
|
# @param profile_id Your Zernio profile ID (get from /v1/profiles). For WhatsApp, a Zernio-provisioned number can only be connected on the profile it was provisioned to; connecting from any other profile is rejected with a 409.
|
|
223
223
|
# @param [Hash] opts the optional parameters
|
|
224
|
-
# @option opts [String] :redirect_url Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, platform_requires_destination,
|
|
224
|
+
# @option opts [String] :redirect_url Your custom redirect URL after connection completes. MUST be an absolute http(s) URL or a custom app scheme for mobile deeplinks (e.g. myapp://callback); a relative path is rejected with 400 INVALID_REDIRECT_URL. Result params are appended with the URL API, so an existing query string is preserved. Standard mode appends connected={platform}&profileId=X&accountId=Y&username=Z. Headless mode appends OAuth data params for platforms requiring selection (e.g. LinkedIn orgs, Facebook pages). If no selection is needed, the account is created directly and the redirect includes accountId. On failure, the browser is sent to the same redirect_url with `error` and `platform` appended. `error` and `platform` are always present. `error_message`, `is_user_fixable`, `reason` and `dashboard_url` are conditional and must be treated as optional. This list is NOT exhaustive and new values may be added at any time. Treat an unrecognized value as a generic failure rather than matching it exhaustively. Existing values are not renamed or removed without notice. OAuth and callback: oauth_denied, invalid_callback, invalid_state, unsupported_platform, connection_failed, internal_error, token_exchange_failed, byok_config_error, personal_account_not_supported, missing_google_permissions, missing_tiktok_permissions, platform_requires_destination, reconnect_account_mismatch, invalid_request Access and limits: profile_not_found, invalid_profile_id, access_denied, account_limit_exceeded, profile_limit_exceeded, payment_required Destination selection: no_facebook_pages, facebook_pages_error, no_google_locations, google_locations_error, google_permission_denied, no_snapchat_public_profiles, snapchat_profiles_error, discord_no_guild, slack_no_team WhatsApp: whatsapp_error, one_whatsapp_per_profile, whatsapp_number_already_connected, whatsapp_number_pinned_to_profile, connection_cancelled Google Ads (platform=googleads): google_ads_auth_failed, google_ads_invalid_state, google_ads_config_error, google_ads_token_failed, google_ads_quota_exhausted, google_ads_callback_error TikTok Ads (platform=tiktokads): tiktok_ads_auth_failed, tiktok_ads_invalid_state, tiktok_ads_access_denied, tiktok_ads_config_error, tiktok_ads_token_failed, tiktok_ads_account_not_found, tiktok_ads_callback_error X Ads (platform=xads): x_ads_denied, x_ads_auth_failed, x_ads_config_error, x_ads_account_not_found, x_ads_state_error, x_ads_token_failed, x_ads_token_missing, x_ads_callback_error Shopify (platform=shopify): shopify_auth_failed, shopify_config_error, shopify_invalid_state, shopify_invalid_hmac, shopify_invalid_shop, shopify_missing_scopes, shopify_callback_error 1. On this endpoint every upstream OAuth error is collapsed into `oauth_denied`. The provider's own value (for example Meta's `access_denied`) is not forwarded. The dedicated ads flows below are different: they use their own denial slugs and `google_ads_auth_failed` and `tiktok_ads_auth_failed` may carry the provider's raw error string in `error_message`. 2. On the tiktok and twitter ads flows `platform` carries the ads platform id (`tiktokads`, `xads`), not the value used in the request path. The googleads and shopify flows report `googleads` and `shopify`. 3. `missing_tiktok_permissions` means the TikTok authorization left out a permission the already-connected account needs, so nothing was changed and it keeps working as before. It is user-fixable: connect again and accept every permission on TikTok's screen.
|
|
225
225
|
# @option opts [Boolean] :headless When true, the user is redirected to your redirect_url with raw OAuth data (code, state) instead of Zernio's default account selection UI. Use this to build a custom connect experience.
|
|
226
226
|
# @option opts [String] :login_method Instagram only. Which of the two Instagram connection methods to use. Ignored for every other platform. `instagram_login` (the default, and what you get if you omit this): the Instagram Login dialog. The user authorizes their Instagram professional account directly, no Facebook Page required. `facebook_login`: the Facebook Login dialog, i.e. \"Instagram API with Facebook Login\". The user authorizes a Facebook Page that has a linked Instagram professional account, and every API call for that account then runs through the Page. Use this when the customer manages Instagram through a Page and expects the Facebook consent screen. Because the user has to pick which Page to connect, the callback continues at the account-selection step, `/v1/connect/instagram/select-account`. `facebook_login` supports `headless=true` like the other selection platforms: the callback redirects to your `redirect_url` with `profileId`, `tempToken`, `platform=instagram`, `step=select_account` and `connect_token`, which you pass into the select-account endpoints to finish. The default `instagram_login` has no selection step, so it connects the account directly.
|
|
227
227
|
# @option opts [String] :onboarding WhatsApp only. Ignored for every other platform. Controls which screen Meta's Embedded Signup popup shows. If omitted, the connection defaults to coexistence (same as `business_app` below), preserving existing behavior for numbers already on the WhatsApp Business app. `api`: standard Embedded Signup, showing Meta's WABA/number picker. Use this to connect a phone number already on Cloud API elsewhere. `business_app`: coexistence, i.e. 'Connect existing WhatsApp Business app' (a number shared between Cloud API and the consumer WhatsApp Business app).
|
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: late-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.935
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenAPI-Generator
|
|
@@ -6367,7 +6367,7 @@ files:
|
|
|
6367
6367
|
- spec/models/you_tube_video_retention_response_retention_curve_inner_spec.rb
|
|
6368
6368
|
- spec/models/you_tube_video_retention_response_spec.rb
|
|
6369
6369
|
- spec/spec_helper.rb
|
|
6370
|
-
- zernio-sdk-0.0.
|
|
6370
|
+
- zernio-sdk-0.0.935.gem
|
|
6371
6371
|
- zernio-sdk.gemspec
|
|
6372
6372
|
homepage: https://openapi-generator.tech
|
|
6373
6373
|
licenses:
|
data/zernio-sdk-0.0.933.gem
DELETED
|
Binary file
|