/*
* 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.603.1
* 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::ErrorResponse),
Status403(models::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
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::ErrorResponse),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status429(models::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
Status404(),
Status409(),
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::ErrorResponse),
Status403(models::ErrorResponse),
Status404(),
Status409(),
Status429(models::ErrorResponse),
Status501(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ad_video_status`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAdVideoStatusError {
Status400(models::ErrorResponse),
Status401(models::ErrorResponse),
Status403(models::ErrorResponse),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
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::ErrorResponse),
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::ErrorResponse),
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::ErrorResponse),
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::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
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::ErrorResponse),
Status403(models::ErrorResponse),
Status404(models::ErrorResponse),
Status409(models::ErrorResponse),
Status501(),
Status502(),
UnknownValue(serde_json::Value),
}
/// Creates a reusable Meta creative from an image or carousel without creating an ad, ready to attach to ads later. The creative goes in the library WITHOUT an ad, reusable on the create endpoints via `existingCreativeId`. ### Source Provide exactly one of: - `imageUrl` (uploaded server-side) - `imageHash` (from POST /v1/ads/images or the library list) - `carouselCards` (2-10 hand-built cards) ### Other fields - The Page (and linked Instagram account, when present) is resolved from `accountId` as the story actor. `instagramAccountId` overrides the Instagram account. - `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 Meta creative from the library, which Meta only allows when no ad uses it. 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 a Meta 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,
}))
}
}
/// Shows how a Meta creative will look in each placement before you create the ad, as embeddable previews. 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,
}))
}
}
/// Shows the details of one Meta creative, such as its content, status and thumbnail. 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>,
thumbnail_width: Option<i32>,
thumbnail_height: Option<i32>,
) -> 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 p_query_thumbnail_width = thumbnail_width;
let p_query_thumbnail_height = thumbnail_height;
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 param_value) = p_query_thumbnail_width {
req_builder = req_builder.query(&[("thumbnailWidth", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_thumbnail_height {
req_builder = req_builder.query(&[("thumbnailHeight", ¶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 fresh, short-lived download links for every image and video a Meta ad uses, including the original media of a boosted Instagram post. The direct signed URLs cover every video and image asset used by the ad's live creative, normalised across shapes: - single image/video - carousel - Reels/Story (`object_story_spec.video_data`) - dynamic creative (`asset_feed_spec`) Video items include Meta's poster thumbnail and the video's Meta id when available. ### Boosted Instagram posts Creatives with no image of their own return the original post's media: the image, the video with its cover as `thumbnailUrl`, or every child of a carousel post with `index`. When the connection cannot read the Instagram post, the item is a 1080px rendering of the creative instead. ### URL lifetime This endpoint 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. **Meta ads that are not synced yet.** Pass `accountId` with a Meta platform ad id that is not in Zernio's store to read its media live through that connection; the response carries `synced: false`.
pub async fn get_ad_media(
configuration: &configuration::Configuration,
ad_id: &str,
account_id: Option<&str>,
) -> Result<models::GetAdMedia200Response, Error<GetAdMediaError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_ad_id = ad_id;
let p_query_account_id = account_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 param_value) = p_query_account_id {
req_builder = req_builder.query(&[("accountId", ¶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::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,
}))
}
}
/// Shows how an existing Meta ad looks in each placement, as embeddable previews. 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. **Meta ads that are not synced yet.** Pass `accountId` with a Meta platform ad id that is not in Zernio's store to render it live through that connection; the response carries `synced: false`.
pub async fn get_ad_previews(
configuration: &configuration::Configuration,
ad_id: &str,
account_id: Option<&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_account_id = account_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_account_id {
req_builder = req_builder.query(&[("accountId", ¶m_value.to_string())]);
}
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,
}))
}
}
/// Checks whether an uploaded Meta ad video has finished processing and is ready to use in an ad. Reads a video's processing state live from Meta (`GET /{video-id}?fields=status`). Poll this after `POST /v1/ads/videos` with `async: true` until `status` is `ready`; the video is only usable as `video.id` on the create endpoints from then on. `status` is normalised: `ready`, `error` (Meta's `error` or `expired`), and `processing` for every other Meta state. `platformStatus` carries Meta's raw `video_status` and `processingProgress` Meta's 0-100 percentage when it reports one. Polling every 5 to 10 seconds is plenty.
pub async fn get_ad_video_status(
configuration: &configuration::Configuration,
video_id: &str,
account_id: &str,
ad_account_id: &str,
) -> Result<models::GetAdVideoStatus200Response, Error<GetAdVideoStatusError>> {
// 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::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::GetAdVideoStatus200Response`"))),
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::GetAdVideoStatus200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAdVideoStatusError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Lists the creatives saved in a Meta ad account so you can reuse one in a new ad. 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 images in a Meta ad account's library so you can reuse one in a creative. 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 videos in a Meta ad account's library, including ones uploaded elsewhere, so you can reuse one across ads. It 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. ### Previewing with `source` `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. ### Videos uploaded elsewhere 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. ### Readiness and uploads 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,
}))
}
}
/// Lists 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) - 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,
}))
}
}
/// Lists a creator's Instagram posts you can run as Partnership Ads, looked up by creator or post link (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,
}))
}
}
/// Lists the creators who have granted, or have pending, permission for your Instagram account to run Partnership Ads with them (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,
}))
}
}
/// Asks a creator for permission to run Partnership Ads with their content, or revokes it (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 Meta creative (Meta does not allow other changes, so make a new creative to change its content). 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 an image file to a Meta ad account's library, for creatives that are not 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,
}))
}
}
/// Uploads a video to a Meta ad account from a URL or file, so you can preview it or use it in ads before any ad exists. This is a standalone ad-video upload (parallel to POST /v1/ads/images). A video creative can then be rendered via POST /v1/ads/preview or attached via `video.id` on POST /v1/ads/create before an ad exists. ### Input Exactly one is required: - `videoUrl`: an https URL we download server-side (SSRF-guarded). - `videoBase64`: raw bytes. Capped by Vercel's body limit, around 4.5 MB payload in practice, so larger videos must come via `videoUrl`. ### Response Returns the Meta `video.id` (reusable wherever `video.id` is accepted) plus Meta's auto-generated poster URL when available. By default the endpoint waits until Meta reports the video ready and answers 201. Chunked upload + transcode can take minutes; the handler runs up to 800 s. ### Async mode Send `async: true` to get a 202 as soon as Meta has accepted the bytes, with `video.status: processing`. Then either: - poll `GET /v1/ads/videos/{videoId}` until `status` is `ready`, or - subscribe to the `ad.video.processed` webhook. A create call that references the video while it is still processing waits up to 30 s, then answers 409 `invalid_resource_state` naming the status endpoint. With `videoUrl` the download and byte transfer still happen inside the request; only Meta's transcode is skipped.
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,
}))
}
}