/*
* Zernio API
*
* API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api
*
* The version of the OpenAPI document: 1.0.4
* Contact: support@zernio.com
* Generated by: https://openapi-generator.tech
*/
use super::{configuration, ContentType, Error};
use crate::{apis::ResponseContent, models};
use reqwest;
use serde::{de::Error as _, Deserialize, Serialize};
/// struct for typed errors of method [`get_billing`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetBillingError {
Status401(models::InlineObject),
Status404(models::InlineObject1),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_calls_usage`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetCallsUsageError {
Status400(),
Status401(models::InlineObject),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_sms_usage`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetSmsUsageError {
Status400(),
Status401(models::InlineObject),
Status404(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_usage`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetUsageError {
Status400(),
Status401(models::InlineObject),
Status404(models::InlineObject1),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_usage_stats`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetUsageStatsError {
Status400(),
Status401(models::InlineObject),
Status404(models::InlineObject1),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_x_api_pricing`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetXApiPricingError {
Status401(models::InlineObject),
UnknownValue(serde_json::Value),
}
/// The billing \"wallet/statement\" view: current plan, billing cycle, accrued balance + remaining credits this period, spend caps, and payment / access status. This is the billing half of the legacy `/v1/usage-stats` snapshot — the per-product consumption half is metering and lives on `GET /v1/usage`. Usage-based (Metronome) accounts get a populated `balance`; legacy Stripe accounts get `balance: null` plus a deprecated `legacy.limits` block and, when payment-blocked, `status.openInvoiceUrl` / `status.declineReason`.
pub async fn get_billing(
configuration: &configuration::Configuration,
) -> Result<models::BillingSnapshot, Error<GetBillingError>> {
let uri_str = format!("{}/v1/billing", configuration.base_path);
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::BillingSnapshot`"))),
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::BillingSnapshot`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetBillingError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Aggregated calling usage across your numbers, both channels (WhatsApp Business Calling + regular phone/PSTN): call counts, answered counts, minutes, and cost. Use it for cost visibility or to rebill your own customers per number. Costs come from each call's billing snapshot, so this endpoint always agrees with the invoice: `billableUSD` is what Zernio bills; `metaUSD` is the WhatsApp per-minute charge Meta bills directly to your WABA (display only, never billed by Zernio). Optional `groupBy` returns a breakdown by UTC day, by your number, or by channel. Defaults to the last 30 days.
pub async fn get_calls_usage(
configuration: &configuration::Configuration,
since: Option<String>,
until: Option<String>,
channel: Option<&str>,
number: Option<&str>,
group_by: Option<&str>,
) -> Result<models::GetCallsUsage200Response, Error<GetCallsUsageError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_since = since;
let p_query_until = until;
let p_query_channel = channel;
let p_query_number = number;
let p_query_group_by = group_by;
let uri_str = format!("{}/v1/usage/calls", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_since {
req_builder = req_builder.query(&[("since", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_until {
req_builder = req_builder.query(&[("until", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_channel {
req_builder = req_builder.query(&[("channel", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_number {
req_builder = req_builder.query(&[("number", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_group_by {
req_builder = req_builder.query(&[("groupBy", ¶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::GetCallsUsage200Response`"))),
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::GetCallsUsage200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetCallsUsageError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Aggregated SMS/MMS volumes across your numbers: sent, received, and total message counts, with an optional breakdown by UTC day or by number. Defaults to the last 30 days. Volumes only, deliberately: SMS cost is carrier-rated asynchronously and billed to your invoice, so per-message cost is not available here. Calling usage (GET /v1/usage/calls) does include billable cost.
pub async fn get_sms_usage(
configuration: &configuration::Configuration,
since: Option<String>,
until: Option<String>,
number: Option<&str>,
group_by: Option<&str>,
) -> Result<models::GetSmsUsage200Response, Error<GetSmsUsageError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_since = since;
let p_query_until = until;
let p_query_number = number;
let p_query_group_by = group_by;
let uri_str = format!("{}/v1/usage/sms", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_since {
req_builder = req_builder.query(&[("since", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_until {
req_builder = req_builder.query(&[("until", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_number {
req_builder = req_builder.query(&[("number", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_group_by {
req_builder = req_builder.query(&[("groupBy", ¶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::GetSmsUsage200Response`"))),
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::GetSmsUsage200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetSmsUsageError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Dual-mode endpoint, selected by query params — fully backward compatible: **Without metering params (the default):** the plan / quota / usage snapshot — plan name, billing period, limits, usage counts, access state. Identical to `GET /v1/usage-stats`. Existing integrations keep working unchanged. **With `range`, `granularity`, `from`, or `to`:** usage METERING — billed spend (USD) by product family (`accounts`, `numbers`, `calls`, `sms`, `dlc`, `xApi`, `credits`, `other`) over the window, at `day` / `month` / `total` granularity, from Metronome's invoice breakdown (the CHARGE view — always reconciles with what gets billed). Also served at `GET /v1/usage/daily`. Usage-based accounts only — legacy Stripe accounts get `{ \"supported\": false, \"days\": [] }`. For per-domain consumption *volumes* use `GET /v1/usage/calls` and `GET /v1/usage/sms`. For the billing statement (balance, credits, caps, payment status) use `GET /v1/billing`.
pub async fn get_usage(
configuration: &configuration::Configuration,
reconcile: Option<bool>,
range: Option<&str>,
from: Option<String>,
to: Option<String>,
granularity: Option<&str>,
) -> Result<models::GetUsage200Response, Error<GetUsageError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_reconcile = reconcile;
let p_query_range = range;
let p_query_from = from;
let p_query_to = to;
let p_query_granularity = granularity;
let uri_str = format!("{}/v1/usage", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_reconcile {
req_builder = req_builder.query(&[("reconcile", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_range {
req_builder = req_builder.query(&[("range", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_from {
req_builder = req_builder.query(&[("from", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_to {
req_builder = req_builder.query(&[("to", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_granularity {
req_builder = req_builder.query(&[("granularity", ¶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::GetUsage200Response`"))),
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::GetUsage200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetUsageError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// The plan / quota / payment-status snapshot: current plan name, billing period, plan limits, usage counts, and access state. Identical to a bare `GET /v1/usage` call (this path is its deprecated alias). For billed spend by product, call `GET /v1/usage` with `range` / `granularity` params. The statement view (balance, credits, caps, payment status) lives at `GET /v1/billing`. The response shape depends on the account's `billingSystem`: * Stripe users: per-period `usage.uploads` / `usage.profiles` counters. * Metronome (usage-based) users: `usage.connectedAccounts`, `usage.xApiCallsByOperation` (per-operation X API call counts — resolve keys via `GET /v1/billing/x-pricing`), plus a `spend` block with `currentPeriodCents`, `xSpendCents`, and `xSpendLimitCents`. The legacy `usage.xApiCalls` 3-tier aggregate is still emitted for back-compat but excludes the $0.200 URL tier and any future tiers — new clients should consume `xApiCallsByOperation` only.
#[deprecated]
pub async fn get_usage_stats(
configuration: &configuration::Configuration,
reconcile: Option<bool>,
) -> Result<models::UsageStats, Error<GetUsageStatsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_reconcile = reconcile;
let uri_str = format!("{}/v1/usage-stats", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_reconcile {
req_builder = req_builder.query(&[("reconcile", ¶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::UsageStats`"))),
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::UsageStats`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetUsageStatsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Returns Zernio's canonical X/Twitter API pricing table. Each X action has its own Metronome product and its own rate, and Zernio passes X API costs through at exact rates with zero markup. The response is identical for every authenticated user (pricing is universal), so it is safe to cache on the client for the duration of a billing period. To compute your own per-operation spend, pair this endpoint with `GET /v1/usage-stats` — that endpoint returns `usage.xApiCallsByOperation` keyed by the same `operation` field you get here.
pub async fn get_x_api_pricing(
configuration: &configuration::Configuration,
) -> Result<models::XApiPricing, Error<GetXApiPricingError>> {
let uri_str = format!("{}/v1/billing/x-pricing", configuration.base_path);
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::XApiPricing`"))),
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::XApiPricing`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetXApiPricingError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}