/*
* 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).
*
* The version of the OpenAPI document: 1.0.4
* 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 {
/// 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,
/// Single-creative shape only. Mutually exclusive with `creatives[]`.
#[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>,
/// Image asset for single-creative shape. Mutually exclusive with `video` and with `creatives[]`. Required on the single-creative shape if `video` is not 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>>,
/// 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`). Each entry must supply its own headline, body, and exactly one of `imageUrl` / `video`.
#[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`, `endDate`, `objective`, `countries`, `interests` and `audienceId` 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>,
/// 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, where the ad set owns it.
#[serde(rename = "budgetAmount", skip_serializing_if = "Option::is_none")]
pub budget_amount: Option<f64>,
/// Required unless `adSetId` is set.
#[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>,
/// ISO 8601 datetime. 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>>,
/// 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>>,
/// 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>,
/// 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>,
/// Where the conversation opens when the ad is tapped.
#[serde(rename = "destination")]
pub destination: Destination,
}
impl CreateMessagingAdRequest {
pub fn new(
account_id: String,
ad_account_id: String,
name: String,
destination: Destination,
) -> CreateMessagingAdRequest {
CreateMessagingAdRequest {
account_id,
ad_account_id,
name,
headline: None,
body: None,
image_url: None,
video: None,
creatives: None,
ad_set_id: None,
budget_amount: None,
budget_type: None,
currency: None,
end_date: None,
countries: None,
cities: None,
regions: None,
zips: None,
metros: None,
custom_locations: None,
age_min: None,
age_max: None,
interests: None,
audience_id: None,
placements: None,
advantage_audience: None,
objective: None,
bid_strategy: None,
bid_amount: None,
roas_average_floor: None,
dsa_beneficiary: None,
dsa_payor: None,
destination,
}
}
}
/// Required unless `adSetId` is set.
#[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'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
}
}
/// 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.
#[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
}
}