/*
* 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.433.2
* 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 CreateAdCampaignRequest {
/// Zernio SocialAccount id (posting or ads variant); its platform decides where the campaign is created.
#[serde(rename = "accountId")]
pub account_id: String,
/// Platform ad account id (Meta act_<n>, Google customer id, LinkedIn account id, ...).
#[serde(rename = "adAccountId")]
pub ad_account_id: String,
#[serde(rename = "name")]
pub name: String,
/// Mapped to the ODAX objective (same mapping as POST /v1/ads/create).
#[serde(rename = "goal")]
pub goal: Goal,
/// Meta app promotion only. Immutable campaign flag. Set true for iOS 14+ SKAdNetwork campaigns and supply promotedObject.applicationId plus promotedObject.objectStoreUrl. The campaign receives promotedObject only when this flag is true. Cannot be changed on an existing campaign.
#[serde(
rename = "isSkadnetworkAttribution",
skip_serializing_if = "Option::is_none"
)]
pub is_skadnetwork_attribution: Option<bool>,
#[serde(rename = "promotedObject", skip_serializing_if = "Option::is_none")]
pub promoted_object: Option<Box<models::AdPromotedObject>>,
/// Every platform buys at auction by default, so this only needs sending on Meta, and only to choose RESERVED. `AUCTION` is accepted on every platform and changes nothing. `RESERVED` (Reach & Frequency) is Meta-only and is rejected with a 400 elsewhere. SKAdNetwork app promotion requires AUCTION.
#[serde(rename = "buyingType", skip_serializing_if = "Option::is_none")]
pub buying_type: Option<BuyingType>,
/// Meta and ChatGPT (OpenAI). Runs campaign validation without creating or persisting a campaign; Idempotency-Key storage is bypassed. Returns HTTP 200 with validateOnly true and status VALIDATED. OpenAI has no dry-run, so Zernio checks the budget, goal and conversion event itself. `true` on any other platform returns 501 `feature_not_available` (same as POST /v1/ads/create); `false` is ignored.
#[serde(rename = "validateOnly", skip_serializing_if = "Option::is_none")]
pub validate_only: Option<bool>,
#[serde(
rename = "specialAdCategories",
skip_serializing_if = "Option::is_none"
)]
pub special_ad_categories: Option<Vec<SpecialAdCategories>>,
/// Campaign-level (CBO) 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. Requires budgetType.
#[serde(rename = "budgetAmount", skip_serializing_if = "Option::is_none")]
pub budget_amount: Option<f64>,
#[serde(rename = "budgetType", skip_serializing_if = "Option::is_none")]
pub budget_type: Option<BudgetType>,
#[serde(rename = "status", skip_serializing_if = "Option::is_none")]
pub status: Option<Status>,
/// Reddit only (400 elsewhere). Campaign start (ISO 8601); a value without an offset is read in the ad account time zone. Requires a campaign budget (`budgetAmount` + `budgetType`, 400 otherwise). Defaults to now.
#[serde(rename = "startDate", skip_serializing_if = "Option::is_none")]
pub start_date: Option<String>,
/// Reddit only (400 elsewhere). Campaign end (ISO 8601); a value without an offset is read in the ad account time zone. Requires a campaign budget (400 otherwise), and is required for `budgetType: lifetime`.
#[serde(rename = "endDate", skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
/// Google only (400 elsewhere). Written on the new campaign.
#[serde(
rename = "locationTargetingType",
skip_serializing_if = "Option::is_none"
)]
pub location_targeting_type: Option<models::GoogleLocationTargetingType>,
/// Campaign bid strategy. **Meta** stores `bid_strategy` alongside the budget, so this REQUIRES `budgetAmount` + `budgetType` on the same request (400 without a campaign budget). A campaign carrying a strategy without its `bid_amount` makes every ad set under it fail (code 100, subcode 1815857), so that state is rejected up front. To bid at ad-set level, set the strategy there instead. **Google:** - LOWEST_COST_WITHOUT_CAP = Maximize Conversions - COST_CAP + bidAmount = Target CPA - LOWEST_COST_WITH_MIN_ROAS + roasAverageFloor = Target ROAS - LOWEST_COST_WITH_BID_CAP + bidAmount = Maximize Clicks with a CPC ceiling - portfolioBidStrategyId attaches a portfolio strategy instead.
#[serde(rename = "bidStrategy", skip_serializing_if = "Option::is_none")]
pub bid_strategy: Option<BidStrategy>,
/// Whole currency units (USD: 5 = $5.00). Required for LOWEST_COST_WITH_BID_CAP and COST_CAP; ignored otherwise. - Meta: validated here but NOT stored, since the campaign object only has bid_strategy. The amount takes effect once an ad set joins this campaign (existingCampaignId on POST /v1/ads/create) and supplies its own bidAmount there. - Google: stored directly on the campaign's bidding strategy.
#[serde(rename = "bidAmount", skip_serializing_if = "Option::is_none")]
pub bid_amount: Option<f64>,
/// Decimal ROAS multiplier (2.0 = 2.0x). Required for LOWEST_COST_WITH_MIN_ROAS.
#[serde(rename = "roasAverageFloor", skip_serializing_if = "Option::is_none")]
pub roas_average_floor: Option<f64>,
/// Google only. Attach an existing portfolio bid strategy (numeric id from GET /v1/ads/bid-strategies) to the new campaign instead of a standard one. Exclusive with bidStrategy.
#[serde(
rename = "portfolioBidStrategyId",
skip_serializing_if = "Option::is_none"
)]
pub portfolio_bid_strategy_id: Option<String>,
/// Google Search only. Target impression share bidding. Exclusive with bidStrategy, portfolioBidStrategyId and manualCpc; bidAmount is refused alongside it (the ceiling is maxCpc).
#[serde(
rename = "targetImpressionShare",
skip_serializing_if = "Option::is_none"
)]
pub target_impression_share: Option<Box<models::GoogleTargetImpressionShare>>,
#[serde(rename = "manualCpc", skip_serializing_if = "Option::is_none")]
pub manual_cpc: Option<Box<models::GoogleManualCpc>>,
#[serde(rename = "networkSettings", skip_serializing_if = "Option::is_none")]
pub network_settings: Option<Box<models::GoogleNetworkSettings>>,
}
impl CreateAdCampaignRequest {
pub fn new(
account_id: String,
ad_account_id: String,
name: String,
goal: Goal,
) -> CreateAdCampaignRequest {
CreateAdCampaignRequest {
account_id,
ad_account_id,
name,
goal,
is_skadnetwork_attribution: None,
promoted_object: None,
buying_type: None,
validate_only: None,
special_ad_categories: None,
budget_amount: None,
budget_type: None,
status: None,
start_date: None,
end_date: None,
location_targeting_type: None,
bid_strategy: None,
bid_amount: None,
roas_average_floor: None,
portfolio_bid_strategy_id: None,
target_impression_share: None,
manual_cpc: None,
network_settings: None,
}
}
}
/// Mapped to the ODAX objective (same mapping as POST /v1/ads/create).
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Goal {
#[serde(rename = "engagement")]
Engagement,
#[serde(rename = "traffic")]
Traffic,
#[serde(rename = "awareness")]
Awareness,
#[serde(rename = "video_views")]
VideoViews,
#[serde(rename = "lead_generation")]
LeadGeneration,
#[serde(rename = "lead_conversion")]
LeadConversion,
#[serde(rename = "job_applicants")]
JobApplicants,
#[serde(rename = "conversions")]
Conversions,
#[serde(rename = "app_promotion")]
AppPromotion,
#[serde(rename = "catalog_sales")]
CatalogSales,
#[serde(rename = "page_likes")]
PageLikes,
#[serde(rename = "page_visits")]
PageVisits,
}
impl Default for Goal {
fn default() -> Goal {
Self::Engagement
}
}
/// Every platform buys at auction by default, so this only needs sending on Meta, and only to choose RESERVED. `AUCTION` is accepted on every platform and changes nothing. `RESERVED` (Reach & Frequency) is Meta-only and is rejected with a 400 elsewhere. SKAdNetwork app promotion requires AUCTION.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum BuyingType {
#[serde(rename = "AUCTION")]
Auction,
#[serde(rename = "RESERVED")]
Reserved,
}
impl Default for BuyingType {
fn default() -> BuyingType {
Self::Auction
}
}
///
#[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
}
}
///
#[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
}
}
///
#[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 bid strategy. **Meta** stores `bid_strategy` alongside the budget, so this REQUIRES `budgetAmount` + `budgetType` on the same request (400 without a campaign budget). A campaign carrying a strategy without its `bid_amount` makes every ad set under it fail (code 100, subcode 1815857), so that state is rejected up front. To bid at ad-set level, set the strategy there instead. **Google:** - LOWEST_COST_WITHOUT_CAP = Maximize Conversions - COST_CAP + bidAmount = Target CPA - LOWEST_COST_WITH_MIN_ROAS + roasAverageFloor = Target ROAS - LOWEST_COST_WITH_BID_CAP + bidAmount = Maximize Clicks with a CPC ceiling - portfolioBidStrategyId attaches a portfolio strategy instead.
#[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
}
}