Skip to main content

hanzo_client/apis/
campaign_api.rs

1/*
2 * Hanzo Cloud API
3 *
4 * The Hanzo Cloud API as a customer calls it: every operation under /v1/ except the operator's admin product, relay routes, legacy spellings and capabilities still reached by flag. Tagged by product: the first path segment after /v1/.
5 *
6 * The version of the OpenAPI document: v1
7 * 
8 * Generated by: https://openapi-generator.tech
9 */
10
11
12use reqwest;
13use serde::{Deserialize, Serialize, de::Error as _};
14use crate::{apis::ResponseContent, models};
15use super::{Error, configuration, ContentType};
16
17
18/// struct for typed errors of method [`delete_campaign_by_id`]
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(untagged)]
21pub enum DeleteCampaignByIdError {
22    UnknownValue(serde_json::Value),
23}
24
25/// struct for typed errors of method [`delete_campaign_by_id_channels_by_kind`]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(untagged)]
28pub enum DeleteCampaignByIdChannelsByKindError {
29    UnknownValue(serde_json::Value),
30}
31
32/// struct for typed errors of method [`get_campaign`]
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(untagged)]
35pub enum GetCampaignError {
36    UnknownValue(serde_json::Value),
37}
38
39/// struct for typed errors of method [`get_campaign_by_id`]
40#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(untagged)]
42pub enum GetCampaignByIdError {
43    UnknownValue(serde_json::Value),
44}
45
46/// struct for typed errors of method [`get_campaign_by_id_metrics`]
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(untagged)]
49pub enum GetCampaignByIdMetricsError {
50    UnknownValue(serde_json::Value),
51}
52
53/// struct for typed errors of method [`get_campaign_summary`]
54#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(untagged)]
56pub enum GetCampaignSummaryError {
57    UnknownValue(serde_json::Value),
58}
59
60/// struct for typed errors of method [`post_campaign`]
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(untagged)]
63pub enum PostCampaignError {
64    UnknownValue(serde_json::Value),
65}
66
67/// struct for typed errors of method [`post_campaign_by_id_channels`]
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(untagged)]
70pub enum PostCampaignByIdChannelsError {
71    UnknownValue(serde_json::Value),
72}
73
74/// struct for typed errors of method [`post_campaign_by_id_launch`]
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[serde(untagged)]
77pub enum PostCampaignByIdLaunchError {
78    UnknownValue(serde_json::Value),
79}
80
81/// struct for typed errors of method [`post_campaign_by_id_pause`]
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(untagged)]
84pub enum PostCampaignByIdPauseError {
85    UnknownValue(serde_json::Value),
86}
87
88/// struct for typed errors of method [`put_campaign_by_id`]
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(untagged)]
91pub enum PutCampaignByIdError {
92    UnknownValue(serde_json::Value),
93}
94
95
96/// Removes one campaign of the caller's org and answers 204 with no body. 404 when the org has no campaign with that id.  It deletes the RECORD, not the executions: a campaign whose channels are live on a provider should be paused first, or those executions keep running with nothing here to report them.
97pub async fn delete_campaign_by_id(configuration: &configuration::Configuration, id: &str) -> Result<(), Error<DeleteCampaignByIdError>> {
98    // add a prefix to parameters to efficiently prevent name collisions
99    let p_id = id;
100
101    let uri_str = format!("{}/v1/campaign/{id}", configuration.base_path, id=crate::apis::urlencode(p_id));
102    let mut req_builder = configuration.client.request(reqwest::Method::DELETE, &uri_str);
103
104    if let Some(ref user_agent) = configuration.user_agent {
105        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
106    }
107    if let Some(ref token) = configuration.bearer_access_token {
108        req_builder = req_builder.bearer_auth(token.to_owned());
109    };
110
111    let req = req_builder.build()?;
112    let resp = configuration.client.execute(req).await?;
113
114    let status = resp.status();
115
116    if !status.is_client_error() && !status.is_server_error() {
117        Ok(())
118    } else {
119        let content = resp.text().await?;
120        let entity: Option<DeleteCampaignByIdError> = serde_json::from_str(&content).ok();
121        Err(Error::ResponseError(ResponseContent { status, content, entity }))
122    }
123}
124
125/// Drops one channel from a campaign and returns the updated campaign. 404 when the campaign carries no channel of that kind.  It removes the channel from the PLAN. A channel that is live at its provider should be paused first — dropping the row here leaves nothing to pause it with afterwards.
126pub async fn delete_campaign_by_id_channels_by_kind(configuration: &configuration::Configuration, id: &str, kind: &str) -> Result<models::CampaignRecord, Error<DeleteCampaignByIdChannelsByKindError>> {
127    // add a prefix to parameters to efficiently prevent name collisions
128    let p_id = id;
129    let p_kind = kind;
130
131    let uri_str = format!("{}/v1/campaign/{id}/channels/{kind}", configuration.base_path, id=crate::apis::urlencode(p_id), kind=crate::apis::urlencode(p_kind));
132    let mut req_builder = configuration.client.request(reqwest::Method::DELETE, &uri_str);
133
134    if let Some(ref user_agent) = configuration.user_agent {
135        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
136    }
137    if let Some(ref token) = configuration.bearer_access_token {
138        req_builder = req_builder.bearer_auth(token.to_owned());
139    };
140
141    let req = req_builder.build()?;
142    let resp = configuration.client.execute(req).await?;
143
144    let status = resp.status();
145    let content_type = resp
146        .headers()
147        .get("content-type")
148        .and_then(|v| v.to_str().ok())
149        .unwrap_or("application/octet-stream");
150    let content_type = super::ContentType::from(content_type);
151
152    if !status.is_client_error() && !status.is_server_error() {
153        let content = resp.text().await?;
154        match content_type {
155            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
156            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CampaignRecord`"))),
157            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::CampaignRecord`")))),
158        }
159    } else {
160        let content = resp.text().await?;
161        let entity: Option<DeleteCampaignByIdChannelsByKindError> = serde_json::from_str(&content).ok();
162        Err(Error::ResponseError(ResponseContent { status, content, entity }))
163    }
164}
165
166/// Returns the org's campaigns, newest first, optionally narrowed to one status.  A campaign is the top-level go-to-market object: a value that SPANS channels (paid, organic, email) and fans out to the executor for each. The listing is org-scoped server-side, so one org can never see another's campaigns.
167pub async fn get_campaign(configuration: &configuration::Configuration, status: Option<&str>, limit: Option<i32>) -> Result<models::CampaignPage, Error<GetCampaignError>> {
168    // add a prefix to parameters to efficiently prevent name collisions
169    let p_status = status;
170    let p_limit = limit;
171
172    let uri_str = format!("{}/v1/campaign", configuration.base_path);
173    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
174
175    if let Some(ref param_value) = p_status {
176        req_builder = req_builder.query(&[("status", &param_value.to_string())]);
177    }
178    if let Some(ref param_value) = p_limit {
179        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
180    }
181    if let Some(ref user_agent) = configuration.user_agent {
182        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
183    }
184    if let Some(ref token) = configuration.bearer_access_token {
185        req_builder = req_builder.bearer_auth(token.to_owned());
186    };
187
188    let req = req_builder.build()?;
189    let resp = configuration.client.execute(req).await?;
190
191    let status = resp.status();
192    let content_type = resp
193        .headers()
194        .get("content-type")
195        .and_then(|v| v.to_str().ok())
196        .unwrap_or("application/octet-stream");
197    let content_type = super::ContentType::from(content_type);
198
199    if !status.is_client_error() && !status.is_server_error() {
200        let content = resp.text().await?;
201        match content_type {
202            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
203            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CampaignPage`"))),
204            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::CampaignPage`")))),
205        }
206    } else {
207        let content = resp.text().await?;
208        let entity: Option<GetCampaignError> = serde_json::from_str(&content).ok();
209        Err(Error::ResponseError(ResponseContent { status, content, entity }))
210    }
211}
212
213/// Returns one campaign of the caller's org — its name, audience, creatives, channels with their per-channel launch state, schedule, budget and status. 404 when the org has no campaign with that id.
214pub async fn get_campaign_by_id(configuration: &configuration::Configuration, id: &str) -> Result<models::CampaignRecord, Error<GetCampaignByIdError>> {
215    // add a prefix to parameters to efficiently prevent name collisions
216    let p_id = id;
217
218    let uri_str = format!("{}/v1/campaign/{id}", configuration.base_path, id=crate::apis::urlencode(p_id));
219    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
220
221    if let Some(ref user_agent) = configuration.user_agent {
222        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
223    }
224    if let Some(ref token) = configuration.bearer_access_token {
225        req_builder = req_builder.bearer_auth(token.to_owned());
226    };
227
228    let req = req_builder.build()?;
229    let resp = configuration.client.execute(req).await?;
230
231    let status = resp.status();
232    let content_type = resp
233        .headers()
234        .get("content-type")
235        .and_then(|v| v.to_str().ok())
236        .unwrap_or("application/octet-stream");
237    let content_type = super::ContentType::from(content_type);
238
239    if !status.is_client_error() && !status.is_server_error() {
240        let content = resp.text().await?;
241        match content_type {
242            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
243            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CampaignRecord`"))),
244            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::CampaignRecord`")))),
245        }
246    } else {
247        let content = resp.text().await?;
248        let entity: Option<GetCampaignByIdError> = serde_json::from_str(&content).ok();
249        Err(Error::ResponseError(ResponseContent { status, content, entity }))
250    }
251}
252
253/// Returns a campaign's results over a window: the analytics funnel (impressions, clicks, conversions, revenue, visitors), the spend each channel's connector reports, and the derived growth KPIs — CTR, CVR, CAC and ROAS.  There is exactly ONE metrics plane and nothing is stored here: the funnel is an analytics query over the campaign's utm_campaign-tagged events, and the spend is each provider's own number read through the org's connector. A warehouse that is not emitting yet degrades to available:false with zeroes — honest-empty, never a 500 and never a fabricated number. When the campaign runs more than one creative and an experiment is wired, abTest carries the A/B analysis.
254pub async fn get_campaign_by_id_metrics(configuration: &configuration::Configuration, id: &str, range: Option<&str>, start: Option<&str>, end: Option<&str>) -> Result<models::CampaignResults, Error<GetCampaignByIdMetricsError>> {
255    // add a prefix to parameters to efficiently prevent name collisions
256    let p_id = id;
257    let p_range = range;
258    let p_start = start;
259    let p_end = end;
260
261    let uri_str = format!("{}/v1/campaign/{id}/metrics", configuration.base_path, id=crate::apis::urlencode(p_id));
262    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
263
264    if let Some(ref param_value) = p_range {
265        req_builder = req_builder.query(&[("range", &param_value.to_string())]);
266    }
267    if let Some(ref param_value) = p_start {
268        req_builder = req_builder.query(&[("start", &param_value.to_string())]);
269    }
270    if let Some(ref param_value) = p_end {
271        req_builder = req_builder.query(&[("end", &param_value.to_string())]);
272    }
273    if let Some(ref user_agent) = configuration.user_agent {
274        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
275    }
276    if let Some(ref token) = configuration.bearer_access_token {
277        req_builder = req_builder.bearer_auth(token.to_owned());
278    };
279
280    let req = req_builder.build()?;
281    let resp = configuration.client.execute(req).await?;
282
283    let status = resp.status();
284    let content_type = resp
285        .headers()
286        .get("content-type")
287        .and_then(|v| v.to_str().ok())
288        .unwrap_or("application/octet-stream");
289    let content_type = super::ContentType::from(content_type);
290
291    if !status.is_client_error() && !status.is_server_error() {
292        let content = resp.text().await?;
293        match content_type {
294            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
295            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CampaignResults`"))),
296            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::CampaignResults`")))),
297        }
298    } else {
299        let content = resp.text().await?;
300        let entity: Option<GetCampaignByIdMetricsError> = serde_json::from_str(&content).ok();
301        Err(Error::ResponseError(ResponseContent { status, content, entity }))
302    }
303}
304
305/// Returns the org's go-to-market roll-up: how many campaigns exist, how many are live, their total budget in cents, and which channel executors this deployment can actually reach.  The channel list is the deployment's honest capability, not a wish: a kind missing from it is one a launch will record as \"unavailable\" rather than fail on.
306pub async fn get_campaign_summary(configuration: &configuration::Configuration, ) -> Result<models::CampaignSummary, Error<GetCampaignSummaryError>> {
307
308    let uri_str = format!("{}/v1/campaign/summary", configuration.base_path);
309    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
310
311    if let Some(ref user_agent) = configuration.user_agent {
312        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
313    }
314    if let Some(ref token) = configuration.bearer_access_token {
315        req_builder = req_builder.bearer_auth(token.to_owned());
316    };
317
318    let req = req_builder.build()?;
319    let resp = configuration.client.execute(req).await?;
320
321    let status = resp.status();
322    let content_type = resp
323        .headers()
324        .get("content-type")
325        .and_then(|v| v.to_str().ok())
326        .unwrap_or("application/octet-stream");
327    let content_type = super::ContentType::from(content_type);
328
329    if !status.is_client_error() && !status.is_server_error() {
330        let content = resp.text().await?;
331        match content_type {
332            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
333            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CampaignSummary`"))),
334            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::CampaignSummary`")))),
335        }
336    } else {
337        let content = resp.text().await?;
338        let entity: Option<GetCampaignSummaryError> = serde_json::from_str(&content).ok();
339        Err(Error::ResponseError(ResponseContent { status, content, entity }))
340    }
341}
342
343/// Creates a campaign as a DRAFT and returns it.  A draft is inert: nothing is sent, no connector is touched and no budget is committed until the campaign is launched. The channels named here are validated and de-duplicated by kind (one executor per kind), and every channel starts \"pending\" whatever the caller claims — a client can never assert a launched state.
344pub async fn post_campaign(configuration: &configuration::Configuration, campaign_write: models::CampaignWrite) -> Result<models::CampaignRecord, Error<PostCampaignError>> {
345    // add a prefix to parameters to efficiently prevent name collisions
346    let p_campaign_write = campaign_write;
347
348    let uri_str = format!("{}/v1/campaign", configuration.base_path);
349    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
350
351    if let Some(ref user_agent) = configuration.user_agent {
352        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
353    }
354    if let Some(ref token) = configuration.bearer_access_token {
355        req_builder = req_builder.bearer_auth(token.to_owned());
356    };
357    req_builder = req_builder.json(&p_campaign_write);
358
359    let req = req_builder.build()?;
360    let resp = configuration.client.execute(req).await?;
361
362    let status = resp.status();
363    let content_type = resp
364        .headers()
365        .get("content-type")
366        .and_then(|v| v.to_str().ok())
367        .unwrap_or("application/octet-stream");
368    let content_type = super::ContentType::from(content_type);
369
370    if !status.is_client_error() && !status.is_server_error() {
371        let content = resp.text().await?;
372        match content_type {
373            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
374            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CampaignRecord`"))),
375            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::CampaignRecord`")))),
376        }
377    } else {
378        let content = resp.text().await?;
379        let entity: Option<PostCampaignError> = serde_json::from_str(&content).ok();
380        Err(Error::ResponseError(ResponseContent { status, content, entity }))
381    }
382}
383
384/// Adds a channel to a campaign, or REPLACES the one it already has of that kind, and returns the updated campaign.  A campaign carries at most one channel per kind, because the kind IS the executor: adding a second \"paid\" channel would mean two ad accounts running one campaign with no way to tell their results apart. The new channel starts \"pending\" — adding it does not launch it.
385pub async fn post_campaign_by_id_channels(configuration: &configuration::Configuration, id: &str, channel_add: models::ChannelAdd) -> Result<models::CampaignRecord, Error<PostCampaignByIdChannelsError>> {
386    // add a prefix to parameters to efficiently prevent name collisions
387    let p_id = id;
388    let p_channel_add = channel_add;
389
390    let uri_str = format!("{}/v1/campaign/{id}/channels", configuration.base_path, id=crate::apis::urlencode(p_id));
391    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
392
393    if let Some(ref user_agent) = configuration.user_agent {
394        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
395    }
396    if let Some(ref token) = configuration.bearer_access_token {
397        req_builder = req_builder.bearer_auth(token.to_owned());
398    };
399    req_builder = req_builder.json(&p_channel_add);
400
401    let req = req_builder.build()?;
402    let resp = configuration.client.execute(req).await?;
403
404    let status = resp.status();
405    let content_type = resp
406        .headers()
407        .get("content-type")
408        .and_then(|v| v.to_str().ok())
409        .unwrap_or("application/octet-stream");
410    let content_type = super::ContentType::from(content_type);
411
412    if !status.is_client_error() && !status.is_server_error() {
413        let content = resp.text().await?;
414        match content_type {
415            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
416            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CampaignRecord`"))),
417            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::CampaignRecord`")))),
418        }
419    } else {
420        let content = resp.text().await?;
421        let entity: Option<PostCampaignByIdChannelsError> = serde_json::from_str(&content).ok();
422        Err(Error::ResponseError(ResponseContent { status, content, entity }))
423    }
424}
425
426/// Pushes the campaign live on each of its channels through that channel's executor and answers the whole campaign with the per-channel outcome written back onto it.  The fan-out is BEST-EFFORT PER CHANNEL, and the honest reading of the result is the rule most callers get wrong: one channel failing never aborts the others, so each channel row carries its own `live`, `failed` or `unavailable` status and detail, and a paid launch can be live while an email launch failed. The campaign itself is `live` when AT LEAST ONE channel launched and `failed` only when none did — `live` is not a claim that every channel launched. Repeating the call is safe: a channel already live is skipped, never re-launched. A campaign carrying more than one creative has its variant assigned here by the experiment client and tagged as `utm_content`.  Org-scoped and fails closed: a valid bearer is required (403 without one), the campaign is read under the caller's OWN org so another tenant's id is a 404, and a campaign with no channels is a 400 — there is nothing to launch. Each executor resolves its own org's connector token from the org passed to it, so a launch can never spend through another tenant's connector.
427pub async fn post_campaign_by_id_launch(configuration: &configuration::Configuration, id: &str) -> Result<(), Error<PostCampaignByIdLaunchError>> {
428    // add a prefix to parameters to efficiently prevent name collisions
429    let p_id = id;
430
431    let uri_str = format!("{}/v1/campaign/{id}/launch", configuration.base_path, id=crate::apis::urlencode(p_id));
432    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
433
434    if let Some(ref user_agent) = configuration.user_agent {
435        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
436    }
437    if let Some(ref token) = configuration.bearer_access_token {
438        req_builder = req_builder.bearer_auth(token.to_owned());
439    };
440
441    let req = req_builder.build()?;
442    let resp = configuration.client.execute(req).await?;
443
444    let status = resp.status();
445
446    if !status.is_client_error() && !status.is_server_error() {
447        Ok(())
448    } else {
449        let content = resp.text().await?;
450        let entity: Option<PostCampaignByIdLaunchError> = serde_json::from_str(&content).ok();
451        Err(Error::ResponseError(ResponseContent { status, content, entity }))
452    }
453}
454
455/// Pauses each live channel on its provider and answers the whole campaign, moved to `paused`, with the per-channel outcome written back onto it.  Only channels that are live and carry a provider reference are touched; a channel whose executor is no longer wired is marked `unavailable` and one whose pause errored is marked `failed`, with the reason on the row. The campaign still reports `paused` in both cases, and that is deliberate rather than sloppy: no live channel remains that this process will meter, and the rows say exactly which provider was not reached so it can be settled by hand.  Org-scoped and fails closed: a valid bearer is required (403 without one) and the campaign is read under the caller's OWN org, so another tenant's id is a 404.
456pub async fn post_campaign_by_id_pause(configuration: &configuration::Configuration, id: &str) -> Result<(), Error<PostCampaignByIdPauseError>> {
457    // add a prefix to parameters to efficiently prevent name collisions
458    let p_id = id;
459
460    let uri_str = format!("{}/v1/campaign/{id}/pause", configuration.base_path, id=crate::apis::urlencode(p_id));
461    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
462
463    if let Some(ref user_agent) = configuration.user_agent {
464        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
465    }
466    if let Some(ref token) = configuration.bearer_access_token {
467        req_builder = req_builder.bearer_auth(token.to_owned());
468    };
469
470    let req = req_builder.build()?;
471    let resp = configuration.client.execute(req).await?;
472
473    let status = resp.status();
474
475    if !status.is_client_error() && !status.is_server_error() {
476        Ok(())
477    } else {
478        let content = resp.text().await?;
479        let entity: Option<PostCampaignByIdPauseError> = serde_json::from_str(&content).ok();
480        Err(Error::ResponseError(ResponseContent { status, content, entity }))
481    }
482}
483
484/// Rewrites a campaign's core fields — name, audience, creatives, schedule and budget — and returns the updated campaign.  Channels are replaced ONLY while the campaign is still a draft. Once it is launched its channels carry provider state (an external id, a live status), so they are added and removed explicitly through the channels sub-resource instead; a whole-object write would silently orphan a running execution.
485pub async fn put_campaign_by_id(configuration: &configuration::Configuration, id: &str, campaign_update: models::CampaignUpdate) -> Result<models::CampaignRecord, Error<PutCampaignByIdError>> {
486    // add a prefix to parameters to efficiently prevent name collisions
487    let p_id = id;
488    let p_campaign_update = campaign_update;
489
490    let uri_str = format!("{}/v1/campaign/{id}", configuration.base_path, id=crate::apis::urlencode(p_id));
491    let mut req_builder = configuration.client.request(reqwest::Method::PUT, &uri_str);
492
493    if let Some(ref user_agent) = configuration.user_agent {
494        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
495    }
496    if let Some(ref token) = configuration.bearer_access_token {
497        req_builder = req_builder.bearer_auth(token.to_owned());
498    };
499    req_builder = req_builder.json(&p_campaign_update);
500
501    let req = req_builder.build()?;
502    let resp = configuration.client.execute(req).await?;
503
504    let status = resp.status();
505    let content_type = resp
506        .headers()
507        .get("content-type")
508        .and_then(|v| v.to_str().ok())
509        .unwrap_or("application/octet-stream");
510    let content_type = super::ContentType::from(content_type);
511
512    if !status.is_client_error() && !status.is_server_error() {
513        let content = resp.text().await?;
514        match content_type {
515            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
516            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CampaignRecord`"))),
517            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::CampaignRecord`")))),
518        }
519    } else {
520        let content = resp.text().await?;
521        let entity: Option<PutCampaignByIdError> = serde_json::from_str(&content).ok();
522        Err(Error::ResponseError(ResponseContent { status, content, entity }))
523    }
524}
525