/*
* 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.142.0
* 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 [`create_ad_creative`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CreateAdCreativeError {
Status400(),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status422(),
Status501(),
Status502(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`delete_ad_creative`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DeleteAdCreativeError {
Status400(),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`delete_ad_video`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DeleteAdVideoError {
Status400(models::ErrorResponse),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`generate_ad_previews`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GenerateAdPreviewsError {
Status400(),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status429(),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ad_creative`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAdCreativeError {
Status400(),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ad_media`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAdMediaError {
Status400(),
Status401(models::InlineObject1),
Status404(),
Status422(),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ad_previews`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAdPreviewsError {
Status400(),
Status401(models::InlineObject1),
Status404(),
Status429(),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_ad_creatives`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListAdCreativesError {
Status400(),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_ad_images`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListAdImagesError {
Status400(),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_ad_videos`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListAdVideosError {
Status400(models::ErrorResponse),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_ads_tik_tok_identities`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListAdsTikTokIdentitiesError {
Status400(models::ErrorResponse),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_partnership_ad_content`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListPartnershipAdContentError {
Status400(models::ErrorResponse),
Status401(models::InlineObject1),
Status403(),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status422(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_partnership_ad_permissions`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListPartnershipAdPermissionsError {
Status400(models::ErrorResponse),
Status401(models::InlineObject1),
Status403(),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status422(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`set_partnership_ad_permission`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SetPartnershipAdPermissionError {
Status400(models::ErrorResponse),
Status401(models::InlineObject1),
Status403(),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status422(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`update_ad_creative`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateAdCreativeError {
Status400(),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`upload_ad_image`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UploadAdImageError {
Status400(),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
Status502(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`upload_ad_video`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UploadAdVideoError {
Status400(),
Status401(models::InlineObject1),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
Status502(),
UnknownValue(serde_json::Value),
}
/// Creates a creative in the library WITHOUT an ad, reusable on the create endpoints via `existingCreativeId`. Provide exactly one of `imageUrl` (uploaded server-side), `imageHash` (from POST /v1/ads/images or the library list), or `carouselCards` (2-10 hand-built cards). The Page (and linked Instagram account, when present) is resolved from `accountId` as the story actor. `creativeFeatures` configures Advantage+ enhancements. `promotion` is not supported and any object is rejected with 400.
pub async fn create_ad_creative(
configuration: &configuration::Configuration,
create_ad_creative_request: models::CreateAdCreativeRequest,
) -> Result<models::CreateAdCreative201Response, Error<CreateAdCreativeError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_create_ad_creative_request = create_ad_creative_request;
let uri_str = format!("{}/v1/ads/creatives", 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_create_ad_creative_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::CreateAdCreative201Response`"))),
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::CreateAdCreative201Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<CreateAdCreativeError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Deletes a creative from the library. Meta only allows deleting creatives not referenced by any ad; otherwise its 400 surfaces verbatim.
pub async fn delete_ad_creative(
configuration: &configuration::Configuration,
creative_id: &str,
account_id: &str,
) -> Result<models::DeleteAdCreative200Response, Error<DeleteAdCreativeError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_creative_id = creative_id;
let p_query_account_id = account_id;
let uri_str = format!(
"{}/v1/ads/creatives/{creativeId}",
configuration.base_path,
creativeId = crate::apis::urlencode(p_path_creative_id)
);
let mut req_builder = configuration
.client
.request(reqwest::Method::DELETE, &uri_str);
req_builder = req_builder.query(&[("accountId", &p_query_account_id.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::DeleteAdCreative200Response`"))),
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::DeleteAdCreative200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<DeleteAdCreativeError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Removes a video from the ad account's video library. Meta's canonical `DELETE /{video_id}` fails with code 10 / subcode 1363055 for videos uploaded via `/act_X/advideos` even with `ads_management`; this endpoint uses the working account-scoped shape `DELETE /act_X/advideos?video_id=<id>` and returns Meta's `{success: true}` verbatim. Deleting a video that lives in a different ad account, or that Meta has already removed, returns Meta's error verbatim as a 4xx.
pub async fn delete_ad_video(
configuration: &configuration::Configuration,
video_id: &str,
account_id: &str,
ad_account_id: &str,
) -> Result<models::DeleteAdVideo200Response, Error<DeleteAdVideoError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_video_id = video_id;
let p_query_account_id = account_id;
let p_query_ad_account_id = ad_account_id;
let uri_str = format!(
"{}/v1/ads/videos/{videoId}",
configuration.base_path,
videoId = crate::apis::urlencode(p_path_video_id)
);
let mut req_builder = configuration
.client
.request(reqwest::Method::DELETE, &uri_str);
req_builder = req_builder.query(&[("accountId", &p_query_account_id.to_string())]);
req_builder = req_builder.query(&[("adAccountId", &p_query_ad_account_id.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::DeleteAdVideo200Response`"))),
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::DeleteAdVideo200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<DeleteAdVideoError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Renders how a creative would look per placement BEFORE any ad exists, via Meta's `/generatepreviews`. Provide exactly one creative source: `existingCreativeId` or `creativeSpec`. Each preview is an HTML `<iframe>` snippet embeddable directly. Unknown `formats` values return Meta's 400 verbatim.
pub async fn generate_ad_previews(
configuration: &configuration::Configuration,
generate_ad_previews_request: models::GenerateAdPreviewsRequest,
) -> Result<models::GenerateAdPreviews200Response, Error<GenerateAdPreviewsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_generate_ad_previews_request = generate_ad_previews_request;
let uri_str = format!("{}/v1/ads/preview", 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_generate_ad_previews_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::GenerateAdPreviews200Response`"))),
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::GenerateAdPreviews200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GenerateAdPreviewsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// One creative's details, verbatim from Meta. `fields` is a raw-passthrough override of the default projection.
pub async fn get_ad_creative(
configuration: &configuration::Configuration,
creative_id: &str,
account_id: &str,
fields: Option<&str>,
) -> Result<models::GetAdCreative200Response, Error<GetAdCreativeError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_creative_id = creative_id;
let p_query_account_id = account_id;
let p_query_fields = fields;
let uri_str = format!(
"{}/v1/ads/creatives/{creativeId}",
configuration.base_path,
creativeId = crate::apis::urlencode(p_path_creative_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::GetAdCreative200Response`"))),
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::GetAdCreative200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAdCreativeError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Returns the direct signed URLs for every video and image asset used by an ad's live creative, normalised across shapes: single image/video, carousel, Reels/Story (`object_story_spec.video_data`) and dynamic creative (`asset_feed_spec`). Video items include Meta's poster thumbnail and the video's Meta id when available. Reads Meta live rather than the stored creative blob because Meta's signed fbcdn URLs carry an `oe=<hex>` expiration (image_url ~24 h, video source ~12 d). Treat URLs as short-lived: re-fetch this endpoint before serving or downloading assets instead of caching URLs beyond that window.
pub async fn get_ad_media(
configuration: &configuration::Configuration,
ad_id: &str,
) -> Result<models::GetAdMedia200Response, Error<GetAdMediaError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_id = ad_id;
let uri_str = format!(
"{}/v1/ads/{adId}/media",
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::GetAdMedia200Response`"))),
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::GetAdMedia200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAdMediaError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Renders an EXISTING ad per placement via Meta's `/{ad_id}/previews`. Each preview is an HTML `<iframe>` snippet embeddable directly. Unknown `formats` values return Meta's 400 verbatim.
pub async fn get_ad_previews(
configuration: &configuration::Configuration,
ad_id: &str,
formats: Option<&str>,
) -> Result<models::GetAdPreviews200Response, Error<GetAdPreviewsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_id = ad_id;
let p_query_formats = formats;
let uri_str = format!(
"{}/v1/ads/{adId}/preview",
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 param_value) = p_query_formats {
req_builder = req_builder.query(&[("formats", ¶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::GetAdPreviews200Response`"))),
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::GetAdPreviews200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAdPreviewsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Lists the ad account's creative library (Meta's `/act_X/adcreatives`), rows returned verbatim. The default projection covers id, name, status, object type, thumbnail, object_story_spec / asset_feed_spec and url_tags; `fields` is a raw-passthrough override. Any creative id here is reusable on the create endpoints via `existingCreativeId`.
pub async fn list_ad_creatives(
configuration: &configuration::Configuration,
account_id: &str,
ad_account_id: &str,
fields: Option<&str>,
limit: Option<i32>,
after: Option<&str>,
) -> Result<models::ListAdCreatives200Response, Error<ListAdCreativesError>> {
// 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_fields = fields;
let p_query_limit = limit;
let p_query_after = after;
let uri_str = format!("{}/v1/ads/creatives", 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())]);
req_builder = req_builder.query(&[("adAccountId", &p_query_ad_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 param_value) = p_query_limit {
req_builder = req_builder.query(&[("limit", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_after {
req_builder = req_builder.query(&[("after", ¶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::ListAdCreatives200Response`"))),
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::ListAdCreatives200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListAdCreativesError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Lists the ad account's image library (Meta's `/act_X/adimages`), rows returned verbatim. The default projection covers hash, url, name, dimensions and status; `fields` is a raw-passthrough override. Any `hash` here is reusable wherever Meta accepts `image_hash` (e.g. `imageHash` on POST /v1/ads/creatives).
pub async fn list_ad_images(
configuration: &configuration::Configuration,
account_id: &str,
ad_account_id: &str,
fields: Option<&str>,
limit: Option<i32>,
after: Option<&str>,
) -> Result<models::ListAdImages200Response, Error<ListAdImagesError>> {
// 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_fields = fields;
let p_query_limit = limit;
let p_query_after = after;
let uri_str = format!("{}/v1/ads/images", 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())]);
req_builder = req_builder.query(&[("adAccountId", &p_query_ad_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 param_value) = p_query_limit {
req_builder = req_builder.query(&[("limit", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_after {
req_builder = req_builder.query(&[("after", ¶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::ListAdImages200Response`"))),
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::ListAdImages200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListAdImagesError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Lists the ad account's video library (Meta's `/act_X/advideos`), rows returned verbatim. The default projection covers id, title, status, poster frames, length and `source` (the playable MP4); `fields` is a raw-passthrough override. Any `id` here is reusable as `video.id` on the create endpoints, so N ads that differ only in copy share one upload. `source` lets you PLAY a video before picking it, which a poster frame alone can't settle when several videos share a first frame. It is a signed CDN URL that EXPIRES, so treat it as good for preview at selection time only. Never persist it; re-list to get a fresh one. This is the only way to reach a video uploaded OUTSIDE Zernio (Ads Manager, another tool); videos we uploaded also come back as `creative.videoId` on GET /v1/ads. Meta transcodes asynchronously, so a row is only usable once `status.video_status` reads `ready`. Upload a new video via POST /v1/ads/videos, or inline via `video.url` on POST /v1/ads/create.
pub async fn list_ad_videos(
configuration: &configuration::Configuration,
account_id: &str,
ad_account_id: &str,
fields: Option<&str>,
limit: Option<i32>,
after: Option<&str>,
) -> Result<models::ListAdVideos200Response, Error<ListAdVideosError>> {
// 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_fields = fields;
let p_query_limit = limit;
let p_query_after = after;
let uri_str = format!("{}/v1/ads/videos", 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())]);
req_builder = req_builder.query(&[("adAccountId", &p_query_ad_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 param_value) = p_query_limit {
req_builder = req_builder.query(&[("limit", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_after {
req_builder = req_builder.query(&[("after", ¶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::ListAdVideos200Response`"))),
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::ListAdVideos200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListAdVideosError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// The identities an ad on this TikTok advertiser may run as (the profile shown on the ad): the advertiser's own TikTok accounts (TT_USER), Business Center authorized accounts (BC_AUTH_TT) and custom brand identities (CUSTOMIZED_USER). Pass the chosen `identityId` on POST /v1/ads/create or POST /v1/ads/boost. Spark-post identities (AUTH_CODE) are not listed; a Spark code creates its own.
pub async fn list_ads_tik_tok_identities(
configuration: &configuration::Configuration,
account_id: &str,
ad_account_id: &str,
) -> Result<models::ListAdsTikTokIdentities200Response, Error<ListAdsTikTokIdentitiesError>> {
// 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 uri_str = format!("{}/v1/ads/tiktok-identities", 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())]);
req_builder = req_builder.query(&[("adAccountId", &p_query_ad_account_id.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::ListAdsTikTokIdentities200Response`"))),
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::ListAdsTikTokIdentities200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListAdsTikTokIdentitiesError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Private beta. Lists creator Instagram posts available to the advertiser for Partnership Ads. Supply creatorUsername or postUrl. Requires instagram_branded_content_ads_brand permission and an advertiser Instagram Business Account.
pub async fn list_partnership_ad_content(
configuration: &configuration::Configuration,
account_id: &str,
creator_username: Option<&str>,
post_url: Option<&str>,
only_allowlisted: Option<bool>,
) -> Result<models::ListPartnershipAdContent200Response, Error<ListPartnershipAdContentError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_account_id = account_id;
let p_query_creator_username = creator_username;
let p_query_post_url = post_url;
let p_query_only_allowlisted = only_allowlisted;
let uri_str = format!("{}/v1/ads/partnership-content", 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_creator_username {
req_builder = req_builder.query(&[("creatorUsername", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_post_url {
req_builder = req_builder.query(&[("postUrl", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_only_allowlisted {
req_builder = req_builder.query(&[("onlyAllowlisted", ¶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::ListPartnershipAdContent200Response`"))),
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::ListPartnershipAdContent200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListPartnershipAdContentError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Private beta. Lists granted or pending creator permissions for the advertiser Instagram Business Account. Requires instagram_branded_content_ads_brand permission.
pub async fn list_partnership_ad_permissions(
configuration: &configuration::Configuration,
account_id: &str,
creator_username: Option<&str>,
) -> Result<models::ListPartnershipAdPermissions200Response, Error<ListPartnershipAdPermissionsError>>
{
// add a prefix to parameters to efficiently prevent name collisions
let p_query_account_id = account_id;
let p_query_creator_username = creator_username;
let uri_str = format!("{}/v1/ads/partnership-permissions", 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_creator_username {
req_builder = req_builder.query(&[("creatorUsername", ¶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::ListPartnershipAdPermissions200Response`"))),
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::ListPartnershipAdPermissions200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListPartnershipAdPermissionsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Private beta. Requests permission from a creator or revokes it when revoke is true. Requests require the creator to approve in Instagram. Requires instagram_branded_content_ads_brand permission.
pub async fn set_partnership_ad_permission(
configuration: &configuration::Configuration,
set_partnership_ad_permission_request: models::SetPartnershipAdPermissionRequest,
) -> Result<models::SetPartnershipAdPermission200Response, Error<SetPartnershipAdPermissionError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_set_partnership_ad_permission_request = set_partnership_ad_permission_request;
let uri_str = format!("{}/v1/ads/partnership-permissions", 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_set_partnership_ad_permission_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::SetPartnershipAdPermission200Response`"))),
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::SetPartnershipAdPermission200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<SetPartnershipAdPermissionError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Renames a creative. Creatives are immutable on Meta beyond `name`. For content changes create a new creative (POST /v1/ads/creatives) and swap it onto the ad (PUT /v1/ads/{adId} with `creative`).
pub async fn update_ad_creative(
configuration: &configuration::Configuration,
creative_id: &str,
update_ad_creative_request: models::UpdateAdCreativeRequest,
) -> Result<models::UpdateAdCreative200Response, Error<UpdateAdCreativeError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_creative_id = creative_id;
let p_body_update_ad_creative_request = update_ad_creative_request;
let uri_str = format!(
"{}/v1/ads/creatives/{creativeId}",
configuration.base_path,
creativeId = crate::apis::urlencode(p_path_creative_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_creative_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::UpdateAdCreative200Response`"))),
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::UpdateAdCreative200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UpdateAdCreativeError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Uploads raw image bytes to the Meta ad account's image library, for callers whose creatives aren't hosted at a public URL. Returns the image `hash` (Meta's identifier for the asset) and the Meta-hosted `url`, which can be used directly as `imageUrl` on the create endpoints. Max 30 MB decoded.
pub async fn upload_ad_image(
configuration: &configuration::Configuration,
upload_ad_image_request: models::UploadAdImageRequest,
) -> Result<models::UploadAdImage201Response, Error<UploadAdImageError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_upload_ad_image_request = upload_ad_image_request;
let uri_str = format!("{}/v1/ads/images", 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_upload_ad_image_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::UploadAdImage201Response`"))),
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::UploadAdImage201Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UploadAdImageError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Standalone ad-video upload (parallel to POST /v1/ads/images), so a video creative can be rendered via POST /v1/ads/preview or attached via `video.id` on POST /v1/ads/create before an ad exists. Accepts either an https `videoUrl` we download server-side (SSRF-guarded) or raw `videoBase64` bytes; exactly one is required. `videoBase64` is capped by Vercel's body limit, around 4.5 MB payload in practice, so larger videos must come via `videoUrl`. Returns the Meta `video.id` (reusable wherever `video.id` is accepted) plus Meta's auto-generated poster URL when available. The endpoint waits until Meta reports the video ready (chunked upload + transcode can take minutes; the handler runs up to 800 s).
pub async fn upload_ad_video(
configuration: &configuration::Configuration,
upload_ad_video_request: models::UploadAdVideoRequest,
) -> Result<models::UploadAdVideo201Response, Error<UploadAdVideoError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_upload_ad_video_request = upload_ad_video_request;
let uri_str = format!("{}/v1/ads/videos", 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_upload_ad_video_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::UploadAdVideo201Response`"))),
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::UploadAdVideo201Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UploadAdVideoError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}