/*
* Zernio API
*
* API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api
*
* The version of the OpenAPI document: 1.0.4
* Contact: support@zernio.com
* Generated by: https://openapi-generator.tech
*/
use super::{configuration, ContentType, Error};
use crate::{apis::ResponseContent, models};
use reqwest;
use serde::{de::Error as _, Deserialize, Serialize};
/// struct for typed errors of method [`boost_post`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum BoostPostError {
Status400(),
Status401(models::InlineObject),
Status403(),
Status422(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`bulk_update_ad_campaign_status`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum BulkUpdateAdCampaignStatusError {
Status400(),
Status401(models::InlineObject),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`create_ad_campaign`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CreateAdCampaignError {
Status400(),
Status401(models::InlineObject),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`create_standalone_ad`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CreateStandaloneAdError {
Status400(),
Status401(models::InlineObject),
Status403(),
Status422(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`delete_ad`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DeleteAdError {
Status401(models::InlineObject),
Status404(models::InlineObject1),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`delete_ad_campaign`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DeleteAdCampaignError {
Status401(models::InlineObject),
Status404(),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`duplicate_ad`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DuplicateAdError {
Status400(),
Status401(models::InlineObject),
Status404(),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`duplicate_ad_campaign`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DuplicateAdCampaignError {
Status400(),
Status401(models::InlineObject),
Status404(),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`duplicate_ad_set`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DuplicateAdSetError {
Status400(),
Status401(models::InlineObject),
Status404(),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ad`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAdError {
Status401(models::InlineObject),
Status404(models::InlineObject1),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ad_set_details`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAdSetDetailsError {
Status400(),
Status401(models::InlineObject),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ad_tree`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAdTreeError {
Status401(models::InlineObject),
Status403(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ads_timeline`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAdsTimelineError {
Status400(models::ErrorResponse),
Status401(models::InlineObject),
Status403(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_ad_campaigns`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListAdCampaignsError {
Status400(models::ErrorResponse),
Status401(models::InlineObject),
Status403(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_ad_keywords`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListAdKeywordsError {
Status400(models::ErrorResponse),
Status401(models::InlineObject),
Status403(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_ads`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListAdsError {
Status400(models::ErrorResponse),
Status401(models::InlineObject),
Status403(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`update_ad`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateAdError {
Status400(),
Status401(models::InlineObject),
Status404(models::InlineObject1),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`update_ad_campaign`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateAdCampaignError {
Status400(),
Status401(models::InlineObject),
Status404(),
Status409(),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`update_ad_campaign_status`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateAdCampaignStatusError {
Status400(),
Status401(models::InlineObject),
Status404(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`update_ad_set`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateAdSetError {
Status400(),
Status401(models::InlineObject),
Status404(),
Status409(),
Status422(),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`update_ad_set_status`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateAdSetStatusError {
Status400(),
Status401(models::InlineObject),
Status404(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`update_ad_status`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateAdStatusError {
Status400(),
Status401(models::InlineObject),
Status404(),
UnknownValue(serde_json::Value),
}
/// Creates a paid ad campaign from an existing published post. Creates the full platform campaign hierarchy (campaign, ad set, ad).
pub async fn boost_post(
configuration: &configuration::Configuration,
boost_post_request: models::BoostPostRequest,
) -> Result<models::UpdateAd200Response, Error<BoostPostError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_boost_post_request = boost_post_request;
let uri_str = format!("{}/v1/ads/boost", configuration.base_path);
let mut req_builder = configuration
.client
.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_boost_post_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UpdateAd200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UpdateAd200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<BoostPostError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Process up to 50 campaigns in one call. Each campaign is updated concurrently and the response contains a per-campaign result so a single bad row does not fail the whole batch.
pub async fn bulk_update_ad_campaign_status(
configuration: &configuration::Configuration,
bulk_update_ad_campaign_status_request: models::BulkUpdateAdCampaignStatusRequest,
) -> Result<models::BulkUpdateAdCampaignStatus200Response, Error<BulkUpdateAdCampaignStatusError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_bulk_update_ad_campaign_status_request = bulk_update_ad_campaign_status_request;
let uri_str = format!("{}/v1/ads/campaigns/bulk-status", configuration.base_path);
let mut req_builder = configuration
.client
.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_bulk_update_ad_campaign_status_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::BulkUpdateAdCampaignStatus200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::BulkUpdateAdCampaignStatus200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<BulkUpdateAdCampaignStatusError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Creates a campaign WITHOUT its first ad set / ad (the ODAX shell only). Ad sets join it later via `existingCampaignId` on the create endpoints. A budget here is campaign-level (CBO) by definition; omit it for ABO (each ad set carries its own budget). Created `PAUSED` unless `status: ACTIVE`. The campaign materializes in `/v1/ads/tree` via the next sync discovery pass. **Idempotency:** send an `Idempotency-Key` header to make retries safe.
pub async fn create_ad_campaign(
configuration: &configuration::Configuration,
create_ad_campaign_request: models::CreateAdCampaignRequest,
idempotency_key: Option<&str>,
) -> Result<models::CreateAdCampaign201Response, Error<CreateAdCampaignError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_create_ad_campaign_request = create_ad_campaign_request;
let p_header_idempotency_key = idempotency_key;
let uri_str = format!("{}/v1/ads/campaigns", configuration.base_path);
let mut req_builder = configuration
.client
.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(param_value) = p_header_idempotency_key {
req_builder = req_builder.header("Idempotency-Key", param_value.to_string());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_create_ad_campaign_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CreateAdCampaign201Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::CreateAdCampaign201Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<CreateAdCampaignError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Creates a paid ad with custom creative across Meta, Google Ads, Pinterest, TikTok, X/Twitter, LinkedIn, and OpenAI Ads (ChatGPT Ads). Supports three mutually-exclusive request shapes selected by the body, a legacy single-creative shape (all platforms, default), a Meta-only multi-creative shape via the creatives array (one ad set with N ads sharing budget and targeting), and an attach shape via adSetId that adds one new ad to an existing ad set, inheriting its budget, targeting, and schedule (Meta, TikTok, and LinkedIn; on LinkedIn adSetId is the existing Campaign id, and the budget, schedule, targeting and bidding fields must be omitted). Per-platform required fields, budget minimums, and video-ad rules are documented on each property below. LinkedIn creates a Single Image or Single Video Ad backed by a Direct Sponsored Content \"dark post\" authored by a Company Page (see `organizationId`); supported goals are engagement, traffic, awareness, and video_views (video ads use the `video` field; video_views requires a video), and traffic ads require `linkUrl`. **Idempotency:** this endpoint is not idempotent at the platform level (a blind retry creates a second campaign/ad set/ad). Send an `Idempotency-Key` header to make retries safe: the first request with a given key creates the ad and we store the response; a retry with the same key replays that exact response (with `Idempotent-Replayed: true`) instead of creating duplicates. Reusing a key with a different body returns 422; a key whose first request is still in flight returns 409 (retry after a short backoff). Keys are scoped to your credential and expire after 24h.
pub async fn create_standalone_ad(
configuration: &configuration::Configuration,
create_standalone_ad_request: models::CreateStandaloneAdRequest,
idempotency_key: Option<&str>,
) -> Result<models::CreateStandaloneAd200Response, Error<CreateStandaloneAdError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_create_standalone_ad_request = create_standalone_ad_request;
let p_header_idempotency_key = idempotency_key;
let uri_str = format!("{}/v1/ads/create", configuration.base_path);
let mut req_builder = configuration
.client
.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(param_value) = p_header_idempotency_key {
req_builder = req_builder.header("Idempotency-Key", param_value.to_string());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_create_standalone_ad_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CreateStandaloneAd200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::CreateStandaloneAd200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<CreateStandaloneAdError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Cancels the ad on the platform and marks it as cancelled in the database. The ad is preserved for history. OpenAI Ads has no delete API; the ad is archived instead (a terminal state, the closest equivalent).
pub async fn delete_ad(
configuration: &configuration::Configuration,
ad_id: &str,
) -> Result<models::DeleteAccountGroup200Response, Error<DeleteAdError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_id = ad_id;
let uri_str = format!(
"{}/v1/ads/{adId}",
configuration.base_path,
adId = crate::apis::urlencode(p_path_ad_id)
);
let mut req_builder = configuration
.client
.request(reqwest::Method::DELETE, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DeleteAccountGroup200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::DeleteAccountGroup200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<DeleteAdError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Deletes the whole campaign on the platform, cascading to its ad sets and ads. Locally, all Ad documents for this campaign are marked `status: cancelled`. Meta-only for now. Other platforms return 501 Not Implemented — fall back to DELETE /v1/ads/{adId} per ad in the meantime. **Empty campaigns.** A campaign with zero ads has no local Ad documents to resolve, so it is invisible to `/v1/ads/tree` and this endpoint would 404. That state is produced by the two-step create flow (campaign, then ads via `existingCampaignId`) whenever Meta rejects the ad step. To delete such a shell, send `accountId` in the body: we skip the local lookup entirely and forward the delete to Meta. `accountId` is ignored when the campaign does have ads.
pub async fn delete_ad_campaign(
configuration: &configuration::Configuration,
campaign_id: &str,
delete_ad_campaign_request: models::DeleteAdCampaignRequest,
) -> Result<models::DeleteAdCampaign200Response, Error<DeleteAdCampaignError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_campaign_id = campaign_id;
let p_body_delete_ad_campaign_request = delete_ad_campaign_request;
let uri_str = format!(
"{}/v1/ads/campaigns/{campaignId}",
configuration.base_path,
campaignId = crate::apis::urlencode(p_path_campaign_id)
);
let mut req_builder = configuration
.client
.request(reqwest::Method::DELETE, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_delete_ad_campaign_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DeleteAdCampaign200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::DeleteAdCampaign200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<DeleteAdCampaignError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Duplicates a single ad via Meta's native `POST /{ad-id}/copies`. The copy is created paused. `adSetId` retargets the copy into another ad set; omitted = the source's own ad set. Accepts the Zernio ad id or the platform ad id. Sync discovery is triggered automatically (`syncAfter: false` to skip).
pub async fn duplicate_ad(
configuration: &configuration::Configuration,
ad_id: &str,
idempotency_key: Option<&str>,
duplicate_ad_request: Option<models::DuplicateAdRequest>,
) -> Result<models::DuplicateAd200Response, Error<DuplicateAdError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_id = ad_id;
let p_header_idempotency_key = idempotency_key;
let p_body_duplicate_ad_request = duplicate_ad_request;
let uri_str = format!(
"{}/v1/ads/{adId}/duplicate",
configuration.base_path,
adId = crate::apis::urlencode(p_path_ad_id)
);
let mut req_builder = configuration
.client
.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(param_value) = p_header_idempotency_key {
req_builder = req_builder.header("Idempotency-Key", param_value.to_string());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_duplicate_ad_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DuplicateAd200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::DuplicateAd200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<DuplicateAdError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Duplicates a campaign, including its ad sets, ads, creatives, and targeting by default (`deepCopy: true`). The copy is created paused so callers can review before launching. Per-platform implementation: - **Meta** uses the native `POST /{campaign-id}/copies` endpoint. - **TikTok** has no native copy primitive; Zernio walks the source graph (`/v2/campaign/get/`, `/v2/adgroup/get/`, `/v2/ad/get/`) and recreates each entity via the corresponding `/create/` endpoints, carrying over budget / targeting / bid_type / bid_price / deep_bid_type / creative fields. Spark Ad linkage (`tiktok_item_id`) is preserved. - **LinkedIn** has no native copy primitive; Zernio walks the source CampaignGroup → Campaigns → Creatives and recreates each entity, carrying over `type` / `costType` / `unitCost` / `optimizationTargetType` / `creativeSelection` / `objectiveType` / `format` / `dailyBudget` / `totalBudget` / `targetingCriteria` / `runSchedule` and every Creative's `content` object verbatim. `statusOption: INHERITED_FROM_SOURCE` is evaluated **per entity**: any Group / Campaign / Creative whose source is `ACTIVE` gets its clone activated too. Duplicating an ACTIVE campaign with `INHERITED_FROM_SOURCE` starts a second front of spend the moment the clone activates — the safe default is `PAUSED`. The new hierarchy is asynchronous to materialize in our DB — we trigger sync discovery automatically. Set `syncAfter: false` to skip and poll `/v1/ads/tree` on your own cadence. Other platforms return 501 Not Implemented.
pub async fn duplicate_ad_campaign(
configuration: &configuration::Configuration,
campaign_id: &str,
duplicate_ad_campaign_request: models::DuplicateAdCampaignRequest,
idempotency_key: Option<&str>,
) -> Result<models::DuplicateAdCampaign200Response, Error<DuplicateAdCampaignError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_campaign_id = campaign_id;
let p_body_duplicate_ad_campaign_request = duplicate_ad_campaign_request;
let p_header_idempotency_key = idempotency_key;
let uri_str = format!(
"{}/v1/ads/campaigns/{campaignId}/duplicate",
configuration.base_path,
campaignId = crate::apis::urlencode(p_path_campaign_id)
);
let mut req_builder = configuration
.client
.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(param_value) = p_header_idempotency_key {
req_builder = req_builder.header("Idempotency-Key", param_value.to_string());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_duplicate_ad_campaign_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DuplicateAdCampaign200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::DuplicateAdCampaign200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<DuplicateAdCampaignError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Duplicates an ad set, including its ads and creatives by default (`deepCopy: true`), via Meta's native `POST /{adset-id}/copies`. The copy is created paused so callers can review before launching. `campaignId` retargets the copy into another campaign; omitted = the source's own campaign. The new hierarchy materializes asynchronously — sync discovery is triggered automatically (`syncAfter: false` to skip).
pub async fn duplicate_ad_set(
configuration: &configuration::Configuration,
ad_set_id: &str,
duplicate_ad_set_request: models::DuplicateAdSetRequest,
idempotency_key: Option<&str>,
) -> Result<models::DuplicateAdSet200Response, Error<DuplicateAdSetError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_set_id = ad_set_id;
let p_body_duplicate_ad_set_request = duplicate_ad_set_request;
let p_header_idempotency_key = idempotency_key;
let uri_str = format!(
"{}/v1/ads/ad-sets/{adSetId}/duplicate",
configuration.base_path,
adSetId = crate::apis::urlencode(p_path_ad_set_id)
);
let mut req_builder = configuration
.client
.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(param_value) = p_header_idempotency_key {
req_builder = req_builder.header("Idempotency-Key", param_value.to_string());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_duplicate_ad_set_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DuplicateAdSet200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::DuplicateAdSet200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<DuplicateAdSetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Returns an ad with its creative, targeting, status, and performance metrics. The `{adId}` path segment accepts any identifier dialect Zernio indexes for the ad: - the Zernio internal `_id` (24-char hex) - Meta's numeric `platformAdId` (the value shipped in `comment.received` webhooks as `comment.ad.id`) - the creative's `effective_object_story_id` (`{pageId}_{postId}` shape, Facebook side) - the creative's `effective_instagram_media_id` (Instagram side) Any of the four resolve to the same ad. Caller doesn't need a translation step.
pub async fn get_ad(
configuration: &configuration::Configuration,
ad_id: &str,
) -> Result<models::GetAd200Response, Error<GetAdError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_id = ad_id;
let uri_str = format!(
"{}/v1/ads/{adId}",
configuration.base_path,
adId = crate::apis::urlencode(p_path_ad_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetAd200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetAd200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAdError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Reads the ad set live from Meta, returned verbatim. The default projection includes `learning_stage_info` (learning-phase status: LEARNING / SUCCESS / FAIL / WAIVING — Meta omits its `status` key on paused ad sets), delivery settings, budgets, schedule and targeting. `fields` is a raw-passthrough override; unknown fields return Meta's 400 verbatim.
pub async fn get_ad_set_details(
configuration: &configuration::Configuration,
ad_set_id: &str,
account_id: &str,
fields: Option<&str>,
) -> Result<models::GetAdSetDetails200Response, Error<GetAdSetDetailsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_set_id = ad_set_id;
let p_query_account_id = account_id;
let p_query_fields = fields;
let uri_str = format!(
"{}/v1/ads/ad-sets/{adSetId}",
configuration.base_path,
adSetId = crate::apis::urlencode(p_path_ad_set_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
if let Some(ref param_value) = p_query_fields {
req_builder = req_builder.query(&[("fields", ¶m_value.to_string())]);
}
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetAdSetDetails200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetAdSetDetails200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAdSetDetailsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Returns a nested Campaign > Ad Set > Ad hierarchy with rolled-up metrics at each level. Uses a two-stage aggregation: ads are grouped into ad sets, then ad sets into campaigns. Metrics are computed over an optional date range, then rolled up from ad level to ad set and campaign levels. Pagination is at the campaign level. Ads without a campaign or ad set ID are grouped into synthetic \"Ungrouped\" buckets. If no date range is provided, defaults to the last 90 days. Date range is capped at 730 days max. Pass `timeIncrement=1` to also get a daily breakdown: each node gains a `daily[]` array of per-day metrics (same fields as the aggregated `metrics`) in the same call. Use `dailyLevel` (`campaign` default, or `adset` / `ad`) to choose which levels carry the series. This replaces calling the tree once per day for per-campaign daily trends. **Deleted objects stay in the tree.** Deleting an ad or a campaign is a soft delete: the Ad documents move to `status: cancelled` and are kept indefinitely, so their historical spend still counts toward the metrics of any date range they fall in. There is no pruning job and no retention window. Filter on `status` if your view should hide them, but do that after reading the totals, not before.
pub async fn get_ad_tree(
configuration: &configuration::Configuration,
page: Option<i32>,
limit: Option<i32>,
source: Option<&str>,
platform: Option<&str>,
status: Option<models::AdStatus>,
ad_account_id: Option<&str>,
page_id: Option<&str>,
account_id: Option<&str>,
profile_id: Option<&str>,
campaign_id: Option<&str>,
from_date: Option<String>,
to_date: Option<String>,
sort: Option<&str>,
time_increment: Option<i32>,
daily_level: Option<&str>,
) -> Result<models::GetAdTree200Response, Error<GetAdTreeError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_page = page;
let p_query_limit = limit;
let p_query_source = source;
let p_query_platform = platform;
let p_query_status = status;
let p_query_ad_account_id = ad_account_id;
let p_query_page_id = page_id;
let p_query_account_id = account_id;
let p_query_profile_id = profile_id;
let p_query_campaign_id = campaign_id;
let p_query_from_date = from_date;
let p_query_to_date = to_date;
let p_query_sort = sort;
let p_query_time_increment = time_increment;
let p_query_daily_level = daily_level;
let uri_str = format!("{}/v1/ads/tree", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_page {
req_builder = req_builder.query(&[("page", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_limit {
req_builder = req_builder.query(&[("limit", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_source {
req_builder = req_builder.query(&[("source", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_platform {
req_builder = req_builder.query(&[("platform", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_status {
req_builder = req_builder.query(&[("status", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_ad_account_id {
req_builder = req_builder.query(&[("adAccountId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_page_id {
req_builder = req_builder.query(&[("pageId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_account_id {
req_builder = req_builder.query(&[("accountId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_profile_id {
req_builder = req_builder.query(&[("profileId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_campaign_id {
req_builder = req_builder.query(&[("campaignId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_from_date {
req_builder = req_builder.query(&[("fromDate", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_to_date {
req_builder = req_builder.query(&[("toDate", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_sort {
req_builder = req_builder.query(&[("sort", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_time_increment {
req_builder = req_builder.query(&[("timeIncrement", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_daily_level {
req_builder = req_builder.query(&[("dailyLevel", ¶m_value.to_string())]);
}
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetAdTree200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetAdTree200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAdTreeError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Returns daily aggregate metrics across all ads in a SocialAccount as a single time series — one row per calendar day in the requested range. Use this for dashboards that draw a daily-spend or daily-conversions chart, instead of calling `/v1/ads/tree` once per day. `accountId` is required. The lookup is sibling-expanded so passing the `metaads` ID also includes ads under the linked `facebook` / `instagram` posting account (and vice-versa) — same convention as `/v1/ads/tree` and `/v1/ads`. Date range defaults to the last 90 days. Capped at 730 days. Ranges older than the ingested history return a `202` immediately with the covered part and `backfillPending: true` while the rest is backfilled in the background; repeat the request shortly until it returns 200 with full data.
pub async fn get_ads_timeline(
configuration: &configuration::Configuration,
account_id: &str,
ad_account_id: Option<&str>,
from_date: Option<String>,
to_date: Option<String>,
platform: Option<&str>,
) -> Result<models::GetAdsTimeline200Response, Error<GetAdsTimelineError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_account_id = account_id;
let p_query_ad_account_id = ad_account_id;
let p_query_from_date = from_date;
let p_query_to_date = to_date;
let p_query_platform = platform;
let uri_str = format!("{}/v1/ads/timeline", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
if let Some(ref param_value) = p_query_ad_account_id {
req_builder = req_builder.query(&[("adAccountId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_from_date {
req_builder = req_builder.query(&[("fromDate", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_to_date {
req_builder = req_builder.query(&[("toDate", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_platform {
req_builder = req_builder.query(&[("platform", ¶m_value.to_string())]);
}
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetAdsTimeline200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetAdsTimeline200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAdsTimelineError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Returns campaigns as virtual aggregations over ad documents grouped by platform campaign ID. Metrics (spend, impressions, clicks, etc.) are summed across all ads in each campaign. Campaign status is derived from child ad statuses (active > pending_review > paused > error > completed > cancelled > rejected).
pub async fn list_ad_campaigns(
configuration: &configuration::Configuration,
include_empty: Option<bool>,
page: Option<i32>,
limit: Option<i32>,
source: Option<&str>,
platform: Option<&str>,
status: Option<models::AdStatus>,
ad_account_id: Option<&str>,
page_id: Option<&str>,
account_id: Option<&str>,
profile_id: Option<&str>,
from_date: Option<String>,
to_date: Option<String>,
) -> Result<models::ListAdCampaigns200Response, Error<ListAdCampaignsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_include_empty = include_empty;
let p_query_page = page;
let p_query_limit = limit;
let p_query_source = source;
let p_query_platform = platform;
let p_query_status = status;
let p_query_ad_account_id = ad_account_id;
let p_query_page_id = page_id;
let p_query_account_id = account_id;
let p_query_profile_id = profile_id;
let p_query_from_date = from_date;
let p_query_to_date = to_date;
let uri_str = format!("{}/v1/ads/campaigns", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_include_empty {
req_builder = req_builder.query(&[("includeEmpty", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_page {
req_builder = req_builder.query(&[("page", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_limit {
req_builder = req_builder.query(&[("limit", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_source {
req_builder = req_builder.query(&[("source", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_platform {
req_builder = req_builder.query(&[("platform", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_status {
req_builder = req_builder.query(&[("status", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_ad_account_id {
req_builder = req_builder.query(&[("adAccountId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_page_id {
req_builder = req_builder.query(&[("pageId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_account_id {
req_builder = req_builder.query(&[("accountId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_profile_id {
req_builder = req_builder.query(&[("profileId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_from_date {
req_builder = req_builder.query(&[("fromDate", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_to_date {
req_builder = req_builder.query(&[("toDate", ¶m_value.to_string())]);
}
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListAdCampaigns200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListAdCampaigns200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListAdCampaignsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Returns the Google Search keyword criteria (positive and negative) synced from connected Google Ads accounts, one row per ad-group keyword. Refreshed about once a week per Google Ads customer (the keyword sweep rides the ads discovery pass on a slower slot, to stay inside Google's shared daily API quota), so keywords added on Google can take several days to appear. A customer synced for the first time is populated on the next discovery pass rather than waiting for its weekly slot, and connecting an account or triggering a manual sync refreshes it immediately. Campaign-level negative keywords are not included; only ad-group-level criteria are.
pub async fn list_ad_keywords(
configuration: &configuration::Configuration,
page: Option<i32>,
limit: Option<i32>,
account_id: Option<&str>,
ad_account_id: Option<&str>,
profile_id: Option<&str>,
campaign_id: Option<&str>,
ad_set_id: Option<&str>,
status: Option<&str>,
match_type: Option<&str>,
negative: Option<bool>,
search: Option<&str>,
) -> Result<models::ListAdKeywords200Response, Error<ListAdKeywordsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_page = page;
let p_query_limit = limit;
let p_query_account_id = account_id;
let p_query_ad_account_id = ad_account_id;
let p_query_profile_id = profile_id;
let p_query_campaign_id = campaign_id;
let p_query_ad_set_id = ad_set_id;
let p_query_status = status;
let p_query_match_type = match_type;
let p_query_negative = negative;
let p_query_search = search;
let uri_str = format!("{}/v1/ads/keywords", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_page {
req_builder = req_builder.query(&[("page", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_limit {
req_builder = req_builder.query(&[("limit", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_account_id {
req_builder = req_builder.query(&[("accountId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_ad_account_id {
req_builder = req_builder.query(&[("adAccountId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_profile_id {
req_builder = req_builder.query(&[("profileId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_campaign_id {
req_builder = req_builder.query(&[("campaignId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_ad_set_id {
req_builder = req_builder.query(&[("adSetId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_status {
req_builder = req_builder.query(&[("status", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_match_type {
req_builder = req_builder.query(&[("matchType", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_negative {
req_builder = req_builder.query(&[("negative", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_search {
req_builder = req_builder.query(&[("search", ¶m_value.to_string())]);
}
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListAdKeywords200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListAdKeywords200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListAdKeywordsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Returns a paginated list of ads with metrics computed over an optional date range. Use source=all to include externally-synced ads from platform ad managers. If no date range is provided, defaults to the last 90 days. Date range is capped at 730 days max. To find the Zernio ad behind a comment you see in Meta Business Manager, filter by platformAdId (the Meta ad ID), effectiveObjectStoryId (Facebook), or effectiveInstagramMediaId (Instagram) — those are the post/media the ad's engagement lives on, and are also returned on each ad's `creative` object. Then call GET /v1/ads/{adId}/comments with the returned ad id.
pub async fn list_ads(
configuration: &configuration::Configuration,
page: Option<i32>,
limit: Option<i32>,
source: Option<&str>,
status: Option<models::AdStatus>,
platform: Option<&str>,
account_id: Option<&str>,
ad_account_id: Option<&str>,
page_id: Option<&str>,
profile_id: Option<&str>,
campaign_id: Option<&str>,
platform_ad_id: Option<&str>,
effective_object_story_id: Option<&str>,
effective_instagram_media_id: Option<&str>,
from_date: Option<String>,
to_date: Option<String>,
) -> Result<models::ListAds200Response, Error<ListAdsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_page = page;
let p_query_limit = limit;
let p_query_source = source;
let p_query_status = status;
let p_query_platform = platform;
let p_query_account_id = account_id;
let p_query_ad_account_id = ad_account_id;
let p_query_page_id = page_id;
let p_query_profile_id = profile_id;
let p_query_campaign_id = campaign_id;
let p_query_platform_ad_id = platform_ad_id;
let p_query_effective_object_story_id = effective_object_story_id;
let p_query_effective_instagram_media_id = effective_instagram_media_id;
let p_query_from_date = from_date;
let p_query_to_date = to_date;
let uri_str = format!("{}/v1/ads", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_page {
req_builder = req_builder.query(&[("page", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_limit {
req_builder = req_builder.query(&[("limit", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_source {
req_builder = req_builder.query(&[("source", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_status {
req_builder = req_builder.query(&[("status", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_platform {
req_builder = req_builder.query(&[("platform", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_account_id {
req_builder = req_builder.query(&[("accountId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_ad_account_id {
req_builder = req_builder.query(&[("adAccountId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_page_id {
req_builder = req_builder.query(&[("pageId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_profile_id {
req_builder = req_builder.query(&[("profileId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_campaign_id {
req_builder = req_builder.query(&[("campaignId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_platform_ad_id {
req_builder = req_builder.query(&[("platformAdId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_effective_object_story_id {
req_builder = req_builder.query(&[("effectiveObjectStoryId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_effective_instagram_media_id {
req_builder = req_builder.query(&[("effectiveInstagramMediaId", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_from_date {
req_builder = req_builder.query(&[("fromDate", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_to_date {
req_builder = req_builder.query(&[("toDate", ¶m_value.to_string())]);
}
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListAds200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListAds200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListAdsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// 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`). - **Pinterest / X / LinkedIn / Google / 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).
pub async fn update_ad(
configuration: &configuration::Configuration,
ad_id: &str,
update_ad_request: models::UpdateAdRequest,
) -> Result<models::UpdateAd200Response, Error<UpdateAdError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_id = ad_id;
let p_body_update_ad_request = update_ad_request;
let uri_str = format!(
"{}/v1/ads/{adId}",
configuration.base_path,
adId = crate::apis::urlencode(p_path_ad_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_update_ad_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UpdateAd200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UpdateAd200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UpdateAdError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Campaign-level edits. At least one of `budget`, `bidStrategy`, `name` or `platformSpecificData` is required. **Empty campaigns.** A campaign with zero ads has no local Ad documents to resolve, so this would 404 even though it exists on Meta. Send `accountId` in the body to skip the local lookup and forward the update to Meta. The response then carries `updated: 0`, since there are no local rows to mirror onto. `accountId` is ignored when the campaign does have ads. - `budget` updates the CBO (Campaign Budget Optimization) budget. For ABO campaigns (where the budget lives on the ad set), use PUT /v1/ads/ad-sets/{adSetId} instead — this endpoint will return 409 with code BUDGET_LEVEL_MISMATCH. - `bidStrategy` sets the campaign-level default bid strategy. Per Meta's spec, `bid_amount` and `bid_constraints` do NOT exist at the campaign level — pass them via PUT /v1/ads/ad-sets/{adSetId}. - `platformSpecificData.spendCap` (Meta only) sets the campaign's lifetime spend cap, in the ad account's currency. Meta-only for now. Other platforms return 501 Not Implemented.
pub async fn update_ad_campaign(
configuration: &configuration::Configuration,
campaign_id: &str,
update_ad_campaign_request: models::UpdateAdCampaignRequest,
) -> Result<models::UpdateAdCampaign200Response, Error<UpdateAdCampaignError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_campaign_id = campaign_id;
let p_body_update_ad_campaign_request = update_ad_campaign_request;
let uri_str = format!(
"{}/v1/ads/campaigns/{campaignId}",
configuration.base_path,
campaignId = crate::apis::urlencode(p_path_campaign_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_update_ad_campaign_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UpdateAdCampaign200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UpdateAdCampaign200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UpdateAdCampaignError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Updates the status of all ads in a campaign. Makes one platform API call (not per-ad) since status cascades through the campaign hierarchy. Ads in terminal statuses (rejected, completed, cancelled) are automatically skipped.
pub async fn update_ad_campaign_status(
configuration: &configuration::Configuration,
campaign_id: &str,
update_ad_campaign_status_request: models::UpdateAdCampaignStatusRequest,
) -> Result<models::UpdateAdCampaignStatus200Response, Error<UpdateAdCampaignStatusError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_campaign_id = campaign_id;
let p_body_update_ad_campaign_status_request = update_ad_campaign_status_request;
let uri_str = format!(
"{}/v1/ads/campaigns/{campaignId}/status",
configuration.base_path,
campaignId = crate::apis::urlencode(p_path_campaign_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_update_ad_campaign_status_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UpdateAdCampaignStatus200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UpdateAdCampaignStatus200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UpdateAdCampaignStatusError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Ad-set-level writes. Use this for ABO budget updates, ad-set-scoped pause/resume, bid-strategy edits, Meta value-rule-set attach/detach, and Meta-only post-launch delivery settings via `platformSpecificData`. At least one updatable field is required. Value rule sets (Meta only, see `/v1/ads/value-rule-sets`): - ATTACH or REPLACE: send `valueRuleSetId`. Attachment is driven by the id's presence, so `valueRulesApplied: true` is optional. Sending a different id replaces the previous association; there is no separate replace call. - DETACH: send `valueRulesApplied: false` and OMIT `valueRuleSetId`. - Sending `valueRulesApplied: false` TOGETHER with `valueRuleSetId` returns 400 `mutually_exclusive_fields`. This is deliberate: Meta attaches the rule set whenever `value_rule_set_id` is present, even with `value_rules_applied` false, so echoing stored state while asking to detach would silently keep the bid adjustments live. - Eligibility: only ad sets on `LOWEST_COST_WITHOUT_CAP` or `COST_CAP`. Meta rejects the rest server-side. - Read back with `GET /v1/ads/ad-sets/{adSetId}?fields=value_rule_set_id`. Meta does not document `value_rules_applied` as a readable ad-set field, so the boolean cannot be read back. Bid strategy compatibility (per Meta's spec): - `LOWEST_COST_WITHOUT_CAP`: no `bidAmount`, no `roasAverageFloor`. - `LOWEST_COST_WITH_BID_CAP` / `COST_CAP`: `bidAmount` REQUIRED (whole currency units). - `LOWEST_COST_WITH_MIN_ROAS`: `roasAverageFloor` REQUIRED (decimal multiplier, e.g. 2.0 = 2.0x ROAS). Delivery settings are validated by Meta against the campaign objective; incompatible combinations (e.g. a billingEvent the optimization goal doesn't allow) surface as 400s from Meta. When updating `budget` on an ABO campaign: if the parent campaign is CBO, the response is 409 with code BUDGET_LEVEL_MISMATCH — route to PUT /v1/ads/campaigns/{campaignId} instead.
pub async fn update_ad_set(
configuration: &configuration::Configuration,
ad_set_id: &str,
update_ad_set_request: models::UpdateAdSetRequest,
) -> Result<models::UpdateAdSet200Response, Error<UpdateAdSetError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_set_id = ad_set_id;
let p_body_update_ad_set_request = update_ad_set_request;
let uri_str = format!(
"{}/v1/ads/ad-sets/{adSetId}",
configuration.base_path,
adSetId = crate::apis::urlencode(p_path_ad_set_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_update_ad_set_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UpdateAdSet200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UpdateAdSet200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UpdateAdSetError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Ad-set-scoped pause/resume (doesn't touch sibling ad sets). Thin wrapper over PUT /v1/ads/ad-sets/{adSetId} for callers that only want the status toggle and prefer a symmetric URL to /v1/ads/campaigns/{campaignId}/status.
pub async fn update_ad_set_status(
configuration: &configuration::Configuration,
ad_set_id: &str,
update_ad_campaign_status_request: models::UpdateAdCampaignStatusRequest,
) -> Result<models::UpdateAdSetStatus200Response, Error<UpdateAdSetStatusError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_set_id = ad_set_id;
let p_body_update_ad_campaign_status_request = update_ad_campaign_status_request;
let uri_str = format!(
"{}/v1/ads/ad-sets/{adSetId}/status",
configuration.base_path,
adSetId = crate::apis::urlencode(p_path_ad_set_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_update_ad_campaign_status_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UpdateAdSetStatus200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UpdateAdSetStatus200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UpdateAdSetStatusError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Ad-scoped pause/resume — touches ONLY this ad, never its parent ad set or campaign (so sibling ads keep running). Thin wrapper over the `status` field of PUT /v1/ads/{adId}, for callers that want a URL symmetric to /v1/ads/campaigns/{campaignId}/status and /v1/ads/ad-sets/{adSetId}/status. `{adId}` accepts the same identifier dialects as GET/PUT /v1/ads/{adId} (Zernio hex `_id`, Meta numeric `platformAdId`, or the creative's effective story/media IDs). `platform` is inferred from the ad, so it's not required in the body. Ads in terminal statuses (rejected, completed, cancelled) and no-op flips (already in the target state) are skipped.
pub async fn update_ad_status(
configuration: &configuration::Configuration,
ad_id: &str,
update_ad_status_request: models::UpdateAdStatusRequest,
) -> Result<models::UpdateAdStatus200Response, Error<UpdateAdStatusError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_id = ad_id;
let p_body_update_ad_status_request = update_ad_status_request;
let uri_str = format!(
"{}/v1/ads/{adId}/status",
configuration.base_path,
adId = crate::apis::urlencode(p_path_ad_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_update_ad_status_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UpdateAdStatus200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UpdateAdStatus200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UpdateAdStatusError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}