/*
* Zernio API
*
* API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api Versioning and deprecation: all endpoints are versioned in the URL path (current version: /v1). Breaking changes only ship in a new path version; existing versions keep working. Deprecated operations are marked 'deprecated: true' in this spec and announced in the changelog (https://zernio.com/changelog) before removal. Errors: every 4xx/5xx response is application/json with a machine-readable 'code' and a human-readable 'error' message (see the ErrorResponse schema). Request ids: responses carry an X-Request-Id header with the id we log the request under. Quote it when reporting a problem. A valid x-request-id you send is reused as that id.
*
* The version of the OpenAPI document: 1.404.0
* Contact: support@zernio.com
* Generated by: https://openapi-generator.tech
*/
use crate::models;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct CreateMessagingAdRequest {
/// Meta enhancement settings for single or attached ads, and defaults for creatives[]. An item replaces the entire map, including with an empty object.
#[serde(rename = "creativeFeatures", skip_serializing_if = "Option::is_none")]
pub creative_features: Option<CreativeFeatures>,
#[serde(rename = "tracking", skip_serializing_if = "Option::is_none")]
pub tracking: Option<Box<models::AdTracking>>,
/// Facebook or Instagram SocialAccount ID.
#[serde(rename = "accountId")]
pub account_id: String,
/// Meta ad account ID, e.g. `act_123456789`.
#[serde(rename = "adAccountId")]
pub ad_account_id: String,
/// Ad display name. Used to derive campaign / ad set names. On the multi-creative shape, each ad's Meta name gets a \" #N\" suffix (1-indexed) so Ads Manager shows them as a numbered batch.
#[serde(rename = "name")]
pub name: String,
/// Exact name for the campaign this request provisions. Omitted keeps `<name> - Campaign`. Ignored with `adSetId` (the ad set already has a campaign).
#[serde(rename = "campaignName", skip_serializing_if = "Option::is_none")]
pub campaign_name: Option<String>,
/// Exact name for the ad set this request provisions. Omitted keeps `<name> - Ad Set`. Ignored with `adSetId`.
#[serde(rename = "adSetName", skip_serializing_if = "Option::is_none")]
pub ad_set_name: Option<String>,
/// Messaging and CTWA only. Platform post or reel ID, the same input boostPost takes as platformPostId. Facebook IDs become object_story_id; Instagram IDs become source_instagram_media_id run as the media owner (resolved from the media on a Meta ads business-login connection, so no Instagram connection is needed). Mutually exclusive with objectStoryId and fresh creative fields.
#[serde(rename = "platformPostId", skip_serializing_if = "Option::is_none")]
pub platform_post_id: Option<String>,
/// Alias of platformPostId, kept for existing callers. Sending both with different values is a 400.
#[serde(rename = "existingPostId", skip_serializing_if = "Option::is_none")]
pub existing_post_id: Option<String>,
/// Messaging and CTWA only. Raw Facebook pageId_postId reference, used as object_story_id even with an Instagram account. Mutually exclusive with platformPostId and fresh creative fields.
#[serde(rename = "objectStoryId", skip_serializing_if = "Option::is_none")]
pub object_story_id: Option<String>,
/// Facebook Page the ad runs as, when the connection was granted several Pages. Defaults to the Page bound to the connection. Any Page granted to the connection is accepted; other ids answer 400 listing the granted Pages. Same semantics as `pageId` on POST /v1/ads/create.
#[serde(rename = "pageId", skip_serializing_if = "Option::is_none")]
pub page_id: Option<String>,
/// WhatsApp only. Optional E.164 number already paired with the Facebook Page. Omit to let Meta select the paired number. Sent to the creative CTA and, when creating a new ad set, its promoted_object. Meta can accept the number yet create the ad set without it; the response then carries a `warnings` entry. Attach requests do not change the existing ad set. Stored as creative.whatsappPhoneNumber on every created ad.
#[serde(
rename = "whatsappPhoneNumber",
skip_serializing_if = "Option::is_none"
)]
pub whatsapp_phone_number: Option<String>,
/// Single-creative shape only. Mutually exclusive with `creatives[]`. Required on a single image or video ad (including one built with `placementAssets`); not used by a `carouselCards` ad and not accepted with an existing post reference.
#[serde(rename = "headline", skip_serializing_if = "Option::is_none")]
pub headline: Option<String>,
/// Primary text shown above the image / video. Single-creative shape only. Mutually exclusive with `creatives[]`.
#[serde(rename = "body", skip_serializing_if = "Option::is_none")]
pub body: Option<String>,
/// Link description, independent of `headline` and `body` (Meta's `link_data.description`, `video_data.link_description` on video, and the shared description of a `placementAssets` feed). Meta shows it mainly on Facebook Feed placements, under the headline, when there is room; Instagram, Stories, Reels and Messenger placements do not display it. Also accepted per entry in `creatives[]`. Not allowed with an existing post creative.
#[serde(rename = "description", skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// Image asset for single-creative shape. Mutually exclusive with `video` and with `creatives[]`. Required on the single-creative shape if neither `video` nor an existing post reference is supplied.
#[serde(rename = "imageUrl", skip_serializing_if = "Option::is_none")]
pub image_url: Option<String>,
#[serde(rename = "video", skip_serializing_if = "Option::is_none")]
pub video: Option<Box<models::CtwaAdRequestBodyVideo>>,
#[serde(rename = "welcomeMessage", skip_serializing_if = "Option::is_none")]
pub welcome_message: Option<Box<models::CtwaAdRequestBodyWelcomeMessage>>,
/// Messaging and CTWA only (a 400 on POST /v1/ads/call). A hand-built carousel of 2-10 image cards, sent to Meta in the order given as `link_data.child_attachments` (by default Meta may reorder them for performance, since `multi_share_optimized` defaults to true; `optimizeCardOrder: false` keeps the order given); every card (and the carousel itself) carries the destination's messaging call to action (WHATSAPP_MESSAGE, MESSAGE_PAGE or INSTAGRAM_MESSAGE), so a tap on any card opens the conversation. Replaces `imageUrl` / `video` (sending either is a 400). `body` is required as the primary text; `headline` is optional and only names the creative (each card has its own `headline`); a top-level `description` is a 400 (set it per card). `welcomeMessage`, `whatsappPhoneNumber` and `destinations` apply as on a single-image ad. Also a 400 with `placementAssets`, `platformPostId`, `existingPostId` or `objectStoryId`. Single-creative shape; for `creatives[]` set it per entry.
#[serde(rename = "carouselCards", skip_serializing_if = "Option::is_none")]
pub carousel_cards: Option<Vec<models::MessagingCarouselCard>>,
/// Only with `carouselCards` (a 400 without them). Sets the creative's `link_data.multi_share_optimized`. `false` keeps the cards in the order given; Meta then takes at most 5 cards, so `false` with more than 5 `carouselCards` is a 400. `true` lets Meta pick and reorder the cards for performance (up to 10). Omitted, nothing is sent and Meta's default (`true`) applies. Single-creative shape; for `creatives[]` set it per entry.
#[serde(rename = "optimizeCardOrder", skip_serializing_if = "Option::is_none")]
pub optimize_card_order: Option<bool>,
/// Multi-creative shape: N CTWA ads under one campaign + one ad set, sharing budget and targeting. Mutually exclusive with the top-level single-creative fields (`headline` / `body` / `imageUrl` / `video`): setting both is a 400, unlike `POST /v1/ads/create` where the top-level fields are silently ignored in multi-creative mode. Each entry supplies headline, body, and image/video, or a platformPostId or objectStoryId reference. Fresh and existing creatives can be mixed.
#[serde(rename = "creatives", skip_serializing_if = "Option::is_none")]
pub creatives: Option<Vec<models::CtwaAdRequestBodyCreativesInner>>,
/// Attach the creatives to this EXISTING messaging ad set instead of building a campaign, so the ad set keeps its learning phase. It then owns budget, targeting and schedule, so `budgetAmount`, `budgetType`, `budgetLevel`, `startDate`, `endDate`, `objective`, `campaignStatus`, `adSetStatus`, `existingCampaignId`, the special ad category fields and every targeting field except `ageMin`, `ageMax`, `placements` and `advantageAudience` are rejected with a 400 alongside it. Its `destination_type` must match the ad's destination.
#[serde(rename = "adSetId", skip_serializing_if = "Option::is_none")]
pub ad_set_id: Option<String>,
/// Create the new messaging ad set (and its ads) under this EXISTING Meta campaign instead of a new one, e.g. several audience ad sets under one campaign. The campaign's objective must be OUTCOME_ENGAGEMENT, OUTCOME_SALES or OUTCOME_LEADS (400 otherwise). If the campaign has a campaign budget, omit `budgetAmount` and `budgetType` (400 if sent); otherwise they are required and land on the new ad set. `objective`, `campaignName`, `campaignStatus`, `budgetLevel`, `specialAdCategories`, `specialAdCategoryCountry` and `adSetId` are rejected alongside it. To add ads to an existing ad set instead, use `adSetId`.
#[serde(rename = "existingCampaignId", skip_serializing_if = "Option::is_none")]
pub existing_campaign_id: Option<String>,
/// Where the budget lives. `adset` (default) puts it on the new ad set. `campaign` creates an Advantage campaign budget (CBO): the budget and bid strategy sit on the campaign and the ad set inherits them, same as POST /v1/ads/create. Not allowed with `adSetId` or `existingCampaignId`.
#[serde(rename = "budgetLevel", skip_serializing_if = "Option::is_none")]
pub budget_level: Option<BudgetLevel>,
/// Budget amount in the ad account's currency major units (e.g. dollars for USD, not cents). Must be > 0. Required unless `adSetId` is set (the ad set owns it) or `existingCampaignId` names a campaign with a campaign budget.
#[serde(rename = "budgetAmount", skip_serializing_if = "Option::is_none")]
pub budget_amount: Option<f64>,
/// Required unless `adSetId` is set or `existingCampaignId` names a campaign with a campaign budget. `lifetime` requires `endDate`.
#[serde(rename = "budgetType", skip_serializing_if = "Option::is_none")]
pub budget_type: Option<BudgetType>,
/// ISO 4217 currency code matching the ad account's currency (e.g. `USD`). 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).
#[serde(rename = "currency", skip_serializing_if = "Option::is_none")]
pub currency: Option<String>,
/// When the ad set starts delivering. ISO 8601 date or date-time. A value with an offset (`2027-01-15T10:00:00+01:00`, `...Z`) is used as is; one without an offset (`2027-01-15T10:00:00`) is read in the ad account's timezone, and a date-only value starts at 00:00 local. Defaults to now.
#[serde(rename = "startDate", skip_serializing_if = "Option::is_none")]
pub start_date: Option<String>,
/// ISO 8601 date or date-time, read like `startDate`; a date-only value ends at 23:59:59 local. Required when `budgetType` is `lifetime`.
#[serde(rename = "endDate", skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
/// ISO 3166-1 alpha-2 country codes. Defaults to `[\"US\"]` only when no other geo (`cities`, `regions`, `zips`, `metros`, `customLocations`) is supplied.
#[serde(rename = "countries", skip_serializing_if = "Option::is_none")]
pub countries: Option<Vec<String>>,
/// City-level geo targeting for local CTWA campaigns. Each entry maps to Meta's TargetingGeoLocationCity. `key` is Meta's city ID. `radius` and `distance_unit` are coupled: set both or neither. Meta enforces a minimum city radius (~17 km / 10 mi); smaller values resolve to a 0-size audience and the ad fails at launch. For a tighter catchment use customLocations (lat/lng).
#[serde(rename = "cities", skip_serializing_if = "Option::is_none")]
pub cities: Option<Vec<models::CtwaAdRequestBodyCitiesInner>>,
/// Region / state-level geo targeting. `key` is Meta's region ID (lookupable via GET /v1/ads/targeting/search?type=region).
#[serde(rename = "regions", skip_serializing_if = "Option::is_none")]
pub regions: Option<Vec<models::CtwaAdRequestBodyRegionsInner>>,
/// ZIP / postal-code geo targeting. `key` is the platform's postal id resolved via /v1/ads/targeting/search.
#[serde(rename = "zips", skip_serializing_if = "Option::is_none")]
pub zips: Option<Vec<models::CtwaAdRequestBodyZipsInner>>,
/// DMA / metro-area geo targeting. `key` is Meta's metro id (e.g. `DMA:807`).
#[serde(rename = "metros", skip_serializing_if = "Option::is_none")]
pub metros: Option<Vec<models::CtwaAdRequestBodyZipsInner>>,
/// Meta only. Continents and trade blocs (`geo_locations.country_groups`), for targeting a whole region without listing its countries. Combines with `countries` rather than replacing it, and is also accepted under `excludedLocations`. Discoverable via `GET /v1/ads/targeting/search?dimension=geo&geoType=country_group`.
#[serde(rename = "countryGroups", skip_serializing_if = "Option::is_none")]
pub country_groups: Option<Vec<CountryGroups>>,
/// Point-radius geo (Meta `geo_locations.custom_locations`). Use for targeting a radius around a specific lat/long when no Meta city/region key fits. `distanceUnit` is required.
#[serde(rename = "customLocations", skip_serializing_if = "Option::is_none")]
pub custom_locations: Option<Vec<models::CreateStandaloneAdRequestCustomLocationsInner>>,
#[serde(rename = "ageMin", skip_serializing_if = "Option::is_none")]
pub age_min: Option<i32>,
#[serde(rename = "ageMax", skip_serializing_if = "Option::is_none")]
pub age_max: Option<i32>,
#[serde(rename = "interests", skip_serializing_if = "Option::is_none")]
pub interests: Option<Vec<models::CreateStandaloneAdRequestBehaviorsInner>>,
/// Custom audience ID to target.
#[serde(rename = "audienceId", skip_serializing_if = "Option::is_none")]
pub audience_id: Option<String>,
#[serde(rename = "placements", skip_serializing_if = "Option::is_none")]
pub placements: Option<Box<models::CtwaAdRequestBodyPlacements>>,
/// Restrict the audience by gender (Meta `genders`). Omit or send all for everyone; all is ignored in adSetId attach mode. Stored on the ad and read back in `targeting.gender`.
#[serde(rename = "gender", skip_serializing_if = "Option::is_none")]
pub gender: Option<Gender>,
/// Audience languages (Meta `locales`). A bare ISO 639-1 code targets all regional variants (\"en\" = all English), a region-qualified code a specific one (\"en_GB\", \"pt_BR\"); unknown codes are rejected.
#[serde(rename = "languages", skip_serializing_if = "Option::is_none")]
pub languages: Option<Vec<String>>,
/// Meta place keys (from GET /v1/ads/targeting/search).
#[serde(rename = "places", skip_serializing_if = "Option::is_none")]
pub places: Option<Vec<models::CtwaAdRequestBodyPlacesInner>>,
/// Meta neighborhood keys (from GET /v1/ads/targeting/search).
#[serde(rename = "neighborhoods", skip_serializing_if = "Option::is_none")]
pub neighborhoods: Option<Vec<models::CtwaAdRequestBodyPlacesInner>>,
/// Geo to exclude, same shape as POST /v1/ads/create (countries, countryGroups, regions, cities, zips, places, neighborhoods, customLocations).
#[serde(rename = "excludedLocations", skip_serializing_if = "Option::is_none")]
pub excluded_locations: Option<std::collections::HashMap<String, serde_json::Value>>,
/// Behavior ids from /v1/ads/targeting/search?dimension=behavior. Meta: each dimension is its own flexible_spec entry (OR within, AND across). TikTok: video/creator interaction categories, sent as the ad group's actions.
#[serde(rename = "behaviors", skip_serializing_if = "Option::is_none")]
pub behaviors: Option<Vec<models::CreateStandaloneAdRequestBehaviorsInner>>,
#[serde(rename = "workPositions", skip_serializing_if = "Option::is_none")]
pub work_positions: Option<Vec<models::CreateStandaloneAdRequestBehaviorsInner>>,
#[serde(rename = "workEmployers", skip_serializing_if = "Option::is_none")]
pub work_employers: Option<Vec<models::CreateStandaloneAdRequestBehaviorsInner>>,
#[serde(rename = "workIndustries", skip_serializing_if = "Option::is_none")]
pub work_industries: Option<Vec<models::CreateStandaloneAdRequestBehaviorsInner>>,
/// Normalized household-income tier, same as POST /v1/ads/create. Incompatible with housing, employment and credit specialAdCategories.
#[serde(rename = "incomeTier", skip_serializing_if = "Option::is_none")]
pub income_tier: Option<IncomeTier>,
/// Meta `user_os`, e.g. [\"iOS_ver_14.0_and_above\"].
#[serde(rename = "userOs", skip_serializing_if = "Option::is_none")]
pub user_os: Option<Vec<String>>,
/// Meta `user_device`.
#[serde(rename = "userDevice", skip_serializing_if = "Option::is_none")]
pub user_device: Option<Vec<String>>,
/// Custom or lookalike audience ids to include.
#[serde(rename = "audienceInclude", skip_serializing_if = "Option::is_none")]
pub audience_include: Option<Vec<String>>,
/// Custom or lookalike audience ids to exclude.
#[serde(rename = "audienceExclude", skip_serializing_if = "Option::is_none")]
pub audience_exclude: Option<Vec<String>>,
/// ID of a saved_targeting audience (POST /v1/ads/audiences), expanded as the base targeting. Precedence: savedTargetingId, then `targeting`, then the flat fields.
#[serde(rename = "savedTargetingId", skip_serializing_if = "Option::is_none")]
pub saved_targeting_id: Option<String>,
/// Nested targeting object, same contract as POST /v1/ads/create and boost. Flat fields win per key.
#[serde(rename = "targeting", skip_serializing_if = "Option::is_none")]
pub targeting: Option<Box<models::TargetingSpec>>,
/// Meta targeting spec sent as the BASE layer of the ad set's `targeting`, exactly as POST /v1/ads/create does: use it for anything the flat fields cannot express, such as a layered `flexible_spec` (entries AND together, ids inside one entry OR). Flat fields you also send are layered on top and win per key. With rawTargeting present the US geo and `advantage_audience: 0` defaults are not injected, so include `targeting_automation` in it (or send `advantageAudience`), as Meta requires it on create.
#[serde(rename = "rawTargeting", skip_serializing_if = "Option::is_none")]
pub raw_targeting: Option<std::collections::HashMap<String, serde_json::Value>>,
/// Meta special ad categories on the new campaign.
#[serde(
rename = "specialAdCategories",
skip_serializing_if = "Option::is_none"
)]
pub special_ad_categories: Option<Vec<SpecialAdCategories>>,
/// Countries the special ad category applies to. Requires specialAdCategories.
#[serde(
rename = "specialAdCategoryCountry",
skip_serializing_if = "Option::is_none"
)]
pub special_ad_category_country: Option<Vec<String>>,
/// Meta's Advantage+ audience expansion. `0` (default) keeps targeting strict; `1` lets Meta expand beyond the supplied targeting when its delivery system finds better matches. Always sent on CREATE (Meta requires it).
#[serde(rename = "advantageAudience", skip_serializing_if = "Option::is_none")]
pub advantage_audience: Option<AdvantageAudience>,
/// Defaults to `OUTCOME_ENGAGEMENT`. `OUTCOME_SALES` and `OUTCOME_LEADS` require additional account configuration (Dataset linked to the WABA for sales) and may be rejected by Meta if missing.
#[serde(rename = "objective", skip_serializing_if = "Option::is_none")]
pub objective: Option<Objective>,
/// Defaults to `ACTIVE`. `PAUSED` pauses only the top-most object this call creates: the new campaign (ad set and ads switched on), or, with `adSetId`, the new ads themselves.
#[serde(rename = "status", skip_serializing_if = "Option::is_none")]
pub status: Option<Status>,
/// Campaign-level status, same semantics as `POST /v1/ads/create`. Defaults to `status`. `PAUSED` holds the new campaign off while the ad set and ads switch on (one resume call brings the whole hierarchy live); `ACTIVE` with `status: PAUSED` switches the campaign on and pauses the new ad set instead. Only meaningful when a new campaign is being created; rejected with a 400 alongside `adSetId` (the attach shape reuses an existing campaign).
#[serde(rename = "campaignStatus", skip_serializing_if = "Option::is_none")]
pub campaign_status: Option<CampaignStatus>,
/// Ad-set-level status, same semantics as `POST /v1/ads/create`. Sets the new ad set's switch alone and overrides `status` for it. A level status (`campaignStatus`, `adSetStatus`, `adStatus`) always wins for its level, and `status: PAUSED` adds a hold of its own only when none of them is PAUSED. Rejected with a 400 alongside `adSetId` (that ad set already exists).
#[serde(rename = "adSetStatus", skip_serializing_if = "Option::is_none")]
pub ad_set_status: Option<AdSetStatus>,
/// Ad-level status, same semantics as `POST /v1/ads/create`. Sets the new ads' switch and overrides `status` for them, also with `adSetId`. Send `campaignStatus`, `adSetStatus` and `adStatus` all PAUSED to create every object paused.
#[serde(rename = "adStatus", skip_serializing_if = "Option::is_none")]
pub ad_status: Option<models::AdActivationStatus>,
/// Meta bid strategy applied to the shared ad set. Defaults to `LOWEST_COST_WITHOUT_CAP` (auto-bid) when omitted. `LOWEST_COST_WITH_BID_CAP` and `COST_CAP` require `bidAmount`. `LOWEST_COST_WITH_MIN_ROAS` requires `roasAverageFloor`. CTWA's `optimization_goal` is fixed to `CONVERSATIONS`, but the bid strategy is independent.
#[serde(rename = "bidStrategy", skip_serializing_if = "Option::is_none")]
pub bid_strategy: Option<BidStrategy>,
/// Whole currency units (e.g. `5` = $5.00 on a USD account). Required when `bidStrategy` is `LOWEST_COST_WITH_BID_CAP` or `COST_CAP`; rejected otherwise.
#[serde(rename = "bidAmount", skip_serializing_if = "Option::is_none")]
pub bid_amount: Option<f64>,
/// Decimal ROAS multiplier (e.g. `2.0` = 2.0× ROAS floor). Required when `bidStrategy` is `LOWEST_COST_WITH_MIN_ROAS`; rejected otherwise. Meta enforces its own upper bound server-side.
#[serde(rename = "roasAverageFloor", skip_serializing_if = "Option::is_none")]
pub roas_average_floor: Option<f64>,
/// Legal entity that benefits from the ad. Required when targeting EU users (EU DSA, Article 26). Optional if the ad account has a default beneficiary: set it once via `PATCH /v1/ads/accounts` or in Meta Ads Manager, and Meta fills it in whenever the field is omitted.
#[serde(rename = "dsaBeneficiary", skip_serializing_if = "Option::is_none")]
pub dsa_beneficiary: Option<String>,
/// Legal entity that pays for the ad. Can differ from `dsaBeneficiary` (for example, an agency paying for a client's ads). Same rules as `dsaBeneficiary`: required for EU targeting unless the ad account has a default payor.
#[serde(rename = "dsaPayor", skip_serializing_if = "Option::is_none")]
pub dsa_payor: Option<String>,
/// Meta only. Regional regulation categories required when the ad set targets certain countries (e.g. AUSTRALIA_FINSERV, AUSTRALIA_UNIVERSAL, BRAZIL_REGULATION, INDIA_FINSERV, MALAYSIA_UNIVERSAL, SINGAPORE_UNIVERSAL, TAIWAN_FINSERV, TAIWAN_UNIVERSAL, THAILAND_UNIVERSAL). Forwarded to the ad set.
#[serde(
rename = "regionalRegulatedCategories",
skip_serializing_if = "Option::is_none"
)]
pub regional_regulated_categories: Option<Vec<String>>,
/// Meta only. Verified identity IDs of the beneficiary and payer for each declared regionalRegulatedCategories value. Meta may require them on ad sets created through the API even when a default beneficiary and payer are set in Ads Manager advertising settings (seen on AUSTRALIA_FINSERV), so sending them explicitly is the reliable path. The value is the Verified identity ID (Meta Business Settings > Verified identities), not a Page or Business ID, and beneficiary and payer may be the same ID. Send each ID as a string of digits: Verified identity IDs can have 17 digits, more than a JSON number holds exactly, and a number that arrives rounded is rejected. Numbers up to 9007199254740991 are still accepted. The IDs reach Meta unchanged. Keys by category: AUSTRALIA_FINSERV uses australia_finserv_beneficiary / australia_finserv_payer; TAIWAN_FINSERV uses taiwan_finserv_beneficiary / taiwan_finserv_payer; INDIA_FINSERV uses india_finserv_beneficiary / india_finserv_payer; TAIWAN_UNIVERSAL uses taiwan_universal_beneficiary / taiwan_universal_payer; SINGAPORE_UNIVERSAL uses singapore_universal_beneficiary / singapore_universal_payer; AUSTRALIA_UNIVERSAL, BRAZIL_REGULATION, MALAYSIA_UNIVERSAL and THAILAND_UNIVERSAL use universal_beneficiary / universal_payer. When Meta rejects the ad set for a missing verified advertiser, the error carries param regionalRegulationIdentities, details.expectedIdentityKeys (the keys the declared categories expect), details.suggestedCategories (the markets Meta ties to that subcode) and Meta's metaCode, metaSubcode and fbtraceId. Ignored when adSetId adds the ad to an existing ad set.
#[serde(
rename = "regionalRegulationIdentities",
skip_serializing_if = "Option::is_none"
)]
pub regional_regulation_identities: Option<
std::collections::HashMap<
String,
models::UpdateAdSetRequestRegionalRegulationIdentitiesValue,
>,
>,
/// Where the conversation opens when the ad is tapped. Set this OR `destinations`, not both.
#[serde(rename = "destination", skip_serializing_if = "Option::is_none")]
pub destination: Option<Destination>,
/// Two or three messaging apps on ONE ad set, like Ads Manager's \"all messaging apps\": the ad set gets Meta's combined destination_type (e.g. MESSAGING_INSTAGRAM_DIRECT_MESSENGER_WHATSAPP) and the creative one CTA per app, so Meta opens the app each viewer is likeliest to answer from. WhatsApp in the list still needs the Page paired with a WhatsApp Business number. With `adSetId`, the existing ad set must already use that combined destination_type. Set this OR `destination`, not both.
#[serde(rename = "destinations", skip_serializing_if = "Option::is_none")]
pub destinations: Option<std::collections::HashSet<Destinations>>,
/// A different image or video per placement on one messaging ad, e.g. a 4:5 image on Feed and a 9:16 image on Stories/Reels. Replaces top-level `imageUrl` / `video` (sending either alongside is a 400); `headline` and `body` stay required as the default copy. The CTA, `welcomeMessage` and `whatsappPhoneNumber` apply to every placement. Works on the single-creative shape and on attach (`adSetId`). Single `destination` only: Meta cannot combine per-placement media with `destinations` (it drops the placement rules from a multi-destination creative, or refuses more than one call to action per placement rule with error 1885878), so that combination is a 400. Also a 400 with `creatives[]`, `platformPostId`, `existingPostId` or `objectStoryId`, and on POST /v1/ads/call.
#[serde(rename = "placementAssets", skip_serializing_if = "Option::is_none")]
pub placement_assets: Option<Box<models::MetaPlacementAssets>>,
/// A workflow in the ad account's profile. When someone clicks the ad, that workflow starts in the conversation the click opens (on the first message, a postback or a standalone referral carrying the ad id), ahead of keyword-matched workflows, if it is active on the receiving account. Stored on the ad. 404 when no such workflow exists in the profile.
#[serde(rename = "workflowId", skip_serializing_if = "Option::is_none")]
pub workflow_id: Option<String>,
/// Dry-runs the ad on Meta with execution_options validate_only as ONE inline campaign + ad set + creative + ad (or creative + ad on the existing ad set with `adSetId`). Nothing is uploaded or created and nothing is stored; media is checked by URL. Supports one creative with `imageUrl`, image `placementAssets`, `carouselCards`, an existing `video.id`, or an existing post. Several creatives, a new `video.url` and video `placementAssets` need uploads first and return 400. Success returns 200 with per-node results; a Meta rejection returns the Meta error.
#[serde(rename = "validateOnly", skip_serializing_if = "Option::is_none")]
pub validate_only: Option<bool>,
}
impl CreateMessagingAdRequest {
pub fn new(
account_id: String,
ad_account_id: String,
name: String,
) -> CreateMessagingAdRequest {
CreateMessagingAdRequest {
creative_features: None,
tracking: None,
account_id,
ad_account_id,
name,
campaign_name: None,
ad_set_name: None,
platform_post_id: None,
existing_post_id: None,
object_story_id: None,
page_id: None,
whatsapp_phone_number: None,
headline: None,
body: None,
description: None,
image_url: None,
video: None,
welcome_message: None,
carousel_cards: None,
optimize_card_order: None,
creatives: None,
ad_set_id: None,
existing_campaign_id: None,
budget_level: None,
budget_amount: None,
budget_type: None,
currency: None,
start_date: None,
end_date: None,
countries: None,
cities: None,
regions: None,
zips: None,
metros: None,
country_groups: None,
custom_locations: None,
age_min: None,
age_max: None,
interests: None,
audience_id: None,
placements: None,
gender: None,
languages: None,
places: None,
neighborhoods: None,
excluded_locations: None,
behaviors: None,
work_positions: None,
work_employers: None,
work_industries: None,
income_tier: None,
user_os: None,
user_device: None,
audience_include: None,
audience_exclude: None,
saved_targeting_id: None,
targeting: None,
raw_targeting: None,
special_ad_categories: None,
special_ad_category_country: None,
advantage_audience: None,
objective: None,
status: None,
campaign_status: None,
ad_set_status: None,
ad_status: None,
bid_strategy: None,
bid_amount: None,
roas_average_floor: None,
dsa_beneficiary: None,
dsa_payor: None,
regional_regulated_categories: None,
regional_regulation_identities: None,
destination: None,
destinations: None,
placement_assets: None,
workflow_id: None,
validate_only: None,
}
}
}
/// Meta enhancement settings for single or attached ads, and defaults for creatives[]. An item replaces the entire map, including with an empty object.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum CreativeFeatures {
#[serde(rename = "OPT_IN")]
OptIn,
#[serde(rename = "OPT_OUT")]
OptOut,
}
impl Default for CreativeFeatures {
fn default() -> CreativeFeatures {
Self::OptIn
}
}
/// Where the budget lives. `adset` (default) puts it on the new ad set. `campaign` creates an Advantage campaign budget (CBO): the budget and bid strategy sit on the campaign and the ad set inherits them, same as POST /v1/ads/create. Not allowed with `adSetId` or `existingCampaignId`.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum BudgetLevel {
#[serde(rename = "adset")]
Adset,
#[serde(rename = "campaign")]
Campaign,
}
impl Default for BudgetLevel {
fn default() -> BudgetLevel {
Self::Adset
}
}
/// Required unless `adSetId` is set or `existingCampaignId` names a campaign with a campaign budget. `lifetime` requires `endDate`.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum BudgetType {
#[serde(rename = "daily")]
Daily,
#[serde(rename = "lifetime")]
Lifetime,
}
impl Default for BudgetType {
fn default() -> BudgetType {
Self::Daily
}
}
/// Meta only. Continents and trade blocs (`geo_locations.country_groups`), for targeting a whole region without listing its countries. Combines with `countries` rather than replacing it, and is also accepted under `excludedLocations`. Discoverable via `GET /v1/ads/targeting/search?dimension=geo&geoType=country_group`.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum CountryGroups {
#[serde(rename = "africa")]
Africa,
#[serde(rename = "asia")]
Asia,
#[serde(rename = "europe")]
Europe,
#[serde(rename = "north_america")]
NorthAmerica,
#[serde(rename = "south_america")]
SouthAmerica,
#[serde(rename = "oceania")]
Oceania,
#[serde(rename = "central_america")]
CentralAmerica,
#[serde(rename = "caribbean")]
Caribbean,
#[serde(rename = "eea")]
Eea,
#[serde(rename = "euro_area")]
EuroArea,
#[serde(rename = "nafta")]
Nafta,
#[serde(rename = "mercosur")]
Mercosur,
#[serde(rename = "afta")]
Afta,
#[serde(rename = "apec")]
Apec,
#[serde(rename = "gcc")]
Gcc,
#[serde(rename = "cisfta")]
Cisfta,
#[serde(rename = "emerging_markets")]
EmergingMarkets,
#[serde(rename = "itunes_app_store")]
ItunesAppStore,
#[serde(rename = "android_free_store")]
AndroidFreeStore,
#[serde(rename = "android_paid_store")]
AndroidPaidStore,
}
impl Default for CountryGroups {
fn default() -> CountryGroups {
Self::Africa
}
}
/// Restrict the audience by gender (Meta `genders`). Omit or send all for everyone; all is ignored in adSetId attach mode. Stored on the ad and read back in `targeting.gender`.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Gender {
#[serde(rename = "all")]
All,
#[serde(rename = "male")]
Male,
#[serde(rename = "female")]
Female,
}
impl Default for Gender {
fn default() -> Gender {
Self::All
}
}
/// Normalized household-income tier, same as POST /v1/ads/create. Incompatible with housing, employment and credit specialAdCategories.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum IncomeTier {
#[serde(rename = "top_5")]
Top5,
#[serde(rename = "top_10")]
Top10,
#[serde(rename = "top_10_25")]
Top1025,
#[serde(rename = "top_25_50")]
Top2550,
}
impl Default for IncomeTier {
fn default() -> IncomeTier {
Self::Top5
}
}
/// Meta special ad categories on the new campaign.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum SpecialAdCategories {
#[serde(rename = "HOUSING")]
Housing,
#[serde(rename = "EMPLOYMENT")]
Employment,
#[serde(rename = "CREDIT")]
Credit,
#[serde(rename = "ISSUES_ELECTIONS_POLITICS")]
IssuesElectionsPolitics,
#[serde(rename = "FINANCIAL_PRODUCTS_SERVICES")]
FinancialProductsServices,
#[serde(rename = "ONLINE_GAMBLING_AND_GAMING")]
OnlineGamblingAndGaming,
}
impl Default for SpecialAdCategories {
fn default() -> SpecialAdCategories {
Self::Housing
}
}
/// Meta's Advantage+ audience expansion. `0` (default) keeps targeting strict; `1` lets Meta expand beyond the supplied targeting when its delivery system finds better matches. Always sent on CREATE (Meta requires it).
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum AdvantageAudience {
#[serde(rename = "0")]
Variant0,
#[serde(rename = "1")]
Variant1,
}
impl Default for AdvantageAudience {
fn default() -> AdvantageAudience {
Self::Variant0
}
}
/// Defaults to `OUTCOME_ENGAGEMENT`. `OUTCOME_SALES` and `OUTCOME_LEADS` require additional account configuration (Dataset linked to the WABA for sales) and may be rejected by Meta if missing.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Objective {
#[serde(rename = "OUTCOME_ENGAGEMENT")]
OutcomeEngagement,
#[serde(rename = "OUTCOME_SALES")]
OutcomeSales,
#[serde(rename = "OUTCOME_LEADS")]
OutcomeLeads,
}
impl Default for Objective {
fn default() -> Objective {
Self::OutcomeEngagement
}
}
/// Defaults to `ACTIVE`. `PAUSED` pauses only the top-most object this call creates: the new campaign (ad set and ads switched on), or, with `adSetId`, the new ads themselves.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Status {
#[serde(rename = "ACTIVE")]
Active,
#[serde(rename = "PAUSED")]
Paused,
}
impl Default for Status {
fn default() -> Status {
Self::Active
}
}
/// Campaign-level status, same semantics as `POST /v1/ads/create`. Defaults to `status`. `PAUSED` holds the new campaign off while the ad set and ads switch on (one resume call brings the whole hierarchy live); `ACTIVE` with `status: PAUSED` switches the campaign on and pauses the new ad set instead. Only meaningful when a new campaign is being created; rejected with a 400 alongside `adSetId` (the attach shape reuses an existing campaign).
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum CampaignStatus {
#[serde(rename = "ACTIVE")]
Active,
#[serde(rename = "PAUSED")]
Paused,
}
impl Default for CampaignStatus {
fn default() -> CampaignStatus {
Self::Active
}
}
/// Ad-set-level status, same semantics as `POST /v1/ads/create`. Sets the new ad set's switch alone and overrides `status` for it. A level status (`campaignStatus`, `adSetStatus`, `adStatus`) always wins for its level, and `status: PAUSED` adds a hold of its own only when none of them is PAUSED. Rejected with a 400 alongside `adSetId` (that ad set already exists).
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum AdSetStatus {
#[serde(rename = "ACTIVE")]
Active,
#[serde(rename = "PAUSED")]
Paused,
}
impl Default for AdSetStatus {
fn default() -> AdSetStatus {
Self::Active
}
}
/// Meta bid strategy applied to the shared ad set. Defaults to `LOWEST_COST_WITHOUT_CAP` (auto-bid) when omitted. `LOWEST_COST_WITH_BID_CAP` and `COST_CAP` require `bidAmount`. `LOWEST_COST_WITH_MIN_ROAS` requires `roasAverageFloor`. CTWA's `optimization_goal` is fixed to `CONVERSATIONS`, but the bid strategy is independent.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum BidStrategy {
#[serde(rename = "LOWEST_COST_WITHOUT_CAP")]
LowestCostWithoutCap,
#[serde(rename = "LOWEST_COST_WITH_BID_CAP")]
LowestCostWithBidCap,
#[serde(rename = "COST_CAP")]
CostCap,
#[serde(rename = "LOWEST_COST_WITH_MIN_ROAS")]
LowestCostWithMinRoas,
}
impl Default for BidStrategy {
fn default() -> BidStrategy {
Self::LowestCostWithoutCap
}
}
/// Where the conversation opens when the ad is tapped. Set this OR `destinations`, not both.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Destination {
#[serde(rename = "whatsapp")]
Whatsapp,
#[serde(rename = "messenger")]
Messenger,
#[serde(rename = "instagram_direct")]
InstagramDirect,
}
impl Default for Destination {
fn default() -> Destination {
Self::Whatsapp
}
}
/// Two or three messaging apps on ONE ad set, like Ads Manager's \"all messaging apps\": the ad set gets Meta's combined destination_type (e.g. MESSAGING_INSTAGRAM_DIRECT_MESSENGER_WHATSAPP) and the creative one CTA per app, so Meta opens the app each viewer is likeliest to answer from. WhatsApp in the list still needs the Page paired with a WhatsApp Business number. With `adSetId`, the existing ad set must already use that combined destination_type. Set this OR `destination`, not both.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Destinations {
#[serde(rename = "whatsapp")]
Whatsapp,
#[serde(rename = "messenger")]
Messenger,
#[serde(rename = "instagram_direct")]
InstagramDirect,
}
impl Default for Destinations {
fn default() -> Destinations {
Self::Whatsapp
}
}