Skip to main content

hanzo_client/apis/
link_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_link_by_id`]
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(untagged)]
21pub enum DeleteLinkByIdError {
22    UnknownValue(serde_json::Value),
23}
24
25/// struct for typed errors of method [`get_link`]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(untagged)]
28pub enum GetLinkError {
29    UnknownValue(serde_json::Value),
30}
31
32/// struct for typed errors of method [`get_link_by_id`]
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(untagged)]
35pub enum GetLinkByIdError {
36    UnknownValue(serde_json::Value),
37}
38
39/// struct for typed errors of method [`get_link_devices_by_machine`]
40#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(untagged)]
42pub enum GetLinkDevicesByMachineError {
43    UnknownValue(serde_json::Value),
44}
45
46/// struct for typed errors of method [`get_link_route`]
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(untagged)]
49pub enum GetLinkRouteError {
50    UnknownValue(serde_json::Value),
51}
52
53/// struct for typed errors of method [`get_link_usage`]
54#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(untagged)]
56pub enum GetLinkUsageError {
57    UnknownValue(serde_json::Value),
58}
59
60/// struct for typed errors of method [`get_link_usage_accounts`]
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(untagged)]
63pub enum GetLinkUsageAccountsError {
64    UnknownValue(serde_json::Value),
65}
66
67/// struct for typed errors of method [`get_link_usage_summary`]
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(untagged)]
70pub enum GetLinkUsageSummaryError {
71    UnknownValue(serde_json::Value),
72}
73
74/// struct for typed errors of method [`post_link`]
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[serde(untagged)]
77pub enum PostLinkError {
78    UnknownValue(serde_json::Value),
79}
80
81/// struct for typed errors of method [`post_link_devices_by_machine_revoke`]
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(untagged)]
84pub enum PostLinkDevicesByMachineRevokeError {
85    UnknownValue(serde_json::Value),
86}
87
88/// struct for typed errors of method [`post_link_usage`]
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(untagged)]
91pub enum PostLinkUsageError {
92    UnknownValue(serde_json::Value),
93}
94
95
96/// Logs out one account and stops the sessions it was running.  It revokes a single linked account and stops the agent sessions that ran under it, answering with the revoked row and how many sessions stopped. The link is RETAINED with a revoked status rather than deleted, so its usage history and the audit trail survive the log-out — which also means a revoked account still appears in the list, and is excluded from the route plan rather than absent from it. The session stop is narrowed to the revoking user's own sessions on that device, provider and account, and a stop that fails does not fail the revoke: the revoked row is the durable truth. An id that does not exist, or belongs to another user or org, is the same 404.
97pub async fn delete_link_by_id(configuration: &configuration::Configuration, id: &str) -> Result<models::RevokeResp, Error<DeleteLinkByIdError>> {
98    // add a prefix to parameters to efficiently prevent name collisions
99    let p_id = id;
100
101    let uri_str = format!("{}/v1/link/{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    let content_type = resp
116        .headers()
117        .get("content-type")
118        .and_then(|v| v.to_str().ok())
119        .unwrap_or("application/octet-stream");
120    let content_type = super::ContentType::from(content_type);
121
122    if !status.is_client_error() && !status.is_server_error() {
123        let content = resp.text().await?;
124        match content_type {
125            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
126            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::RevokeResp`"))),
127            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::RevokeResp`")))),
128        }
129    } else {
130        let content = resp.text().await?;
131        let entity: Option<DeleteLinkByIdError> = serde_json::from_str(&content).ok();
132        Err(Error::ResponseError(ResponseContent { status, content, entity }))
133    }
134}
135
136/// Lists your linked accounts and the devices they sit on.  It answers the caller's own links plus a devices projection of the same rows folded per machine — the cross-machine \"AI Providers / Accounts\" view. A device is a projection, not a stored entity: its labels come from its most-recently-seen account, so there is no device to create and none to garbage-collect. Revoked links are INCLUDED rather than dropped, because a logged-out account keeps its usage history and audit trail. Scoped to the caller: a validated principal and a non-empty org, else 403.
137pub async fn get_link(configuration: &configuration::Configuration, ) -> Result<models::LinkList, Error<GetLinkError>> {
138
139    let uri_str = format!("{}/v1/link", configuration.base_path);
140    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
141
142    if let Some(ref user_agent) = configuration.user_agent {
143        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
144    }
145    if let Some(ref token) = configuration.bearer_access_token {
146        req_builder = req_builder.bearer_auth(token.to_owned());
147    };
148
149    let req = req_builder.build()?;
150    let resp = configuration.client.execute(req).await?;
151
152    let status = resp.status();
153    let content_type = resp
154        .headers()
155        .get("content-type")
156        .and_then(|v| v.to_str().ok())
157        .unwrap_or("application/octet-stream");
158    let content_type = super::ContentType::from(content_type);
159
160    if !status.is_client_error() && !status.is_server_error() {
161        let content = resp.text().await?;
162        match content_type {
163            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
164            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::LinkList`"))),
165            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::LinkList`")))),
166        }
167    } else {
168        let content = resp.text().await?;
169        let entity: Option<GetLinkError> = serde_json::from_str(&content).ok();
170        Err(Error::ResponseError(ResponseContent { status, content, entity }))
171    }
172}
173
174/// Reads one linked account.  It answers a single link — its device, provider, account, plan, how it bills, its status and its latest usage snapshot. An id that does not exist, or belongs to another user or org, is the same 404: the scope is a bound predicate on the read, so a wrong id and a foreign id are indistinguishable and neither confirms the other's existence. The static paths on this collection — route, usage, devices — register before this one and win first-match, so a link whose id collided with one of those words could not be addressed here.
175pub async fn get_link_by_id(configuration: &configuration::Configuration, id: &str) -> Result<models::LinkView, Error<GetLinkByIdError>> {
176    // add a prefix to parameters to efficiently prevent name collisions
177    let p_id = id;
178
179    let uri_str = format!("{}/v1/link/{id}", configuration.base_path, id=crate::apis::urlencode(p_id));
180    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
181
182    if let Some(ref user_agent) = configuration.user_agent {
183        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
184    }
185    if let Some(ref token) = configuration.bearer_access_token {
186        req_builder = req_builder.bearer_auth(token.to_owned());
187    };
188
189    let req = req_builder.build()?;
190    let resp = configuration.client.execute(req).await?;
191
192    let status = resp.status();
193    let content_type = resp
194        .headers()
195        .get("content-type")
196        .and_then(|v| v.to_str().ok())
197        .unwrap_or("application/octet-stream");
198    let content_type = super::ContentType::from(content_type);
199
200    if !status.is_client_error() && !status.is_server_error() {
201        let content = resp.text().await?;
202        match content_type {
203            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
204            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::LinkView`"))),
205            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::LinkView`")))),
206        }
207    } else {
208        let content = resp.text().await?;
209        let entity: Option<GetLinkByIdError> = serde_json::from_str(&content).ok();
210        Err(Error::ResponseError(ResponseContent { status, content, entity }))
211    }
212}
213
214/// Shows one machine: its accounts, usage and live sessions.  It answers one device — its host and OS labels, every account the caller has signed in on that machine with its latest usage, and how many agent sessions the caller currently has running on it. The device labels come from the most-recently-seen account, since a device is a projection of its links rather than a row of its own. A machine with none of the caller's accounts is 404, which is also the answer when the machine belongs to someone else — the scope makes the two indistinguishable, deliberately. The session count reports 0 where the agent plane is not mounted rather than failing the read.
215pub async fn get_link_devices_by_machine(configuration: &configuration::Configuration, machine: &str) -> Result<models::DeviceView, Error<GetLinkDevicesByMachineError>> {
216    // add a prefix to parameters to efficiently prevent name collisions
217    let p_machine = machine;
218
219    let uri_str = format!("{}/v1/link/devices/{machine}", configuration.base_path, machine=crate::apis::urlencode(p_machine));
220    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
221
222    if let Some(ref user_agent) = configuration.user_agent {
223        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
224    }
225    if let Some(ref token) = configuration.bearer_access_token {
226        req_builder = req_builder.bearer_auth(token.to_owned());
227    };
228
229    let req = req_builder.build()?;
230    let resp = configuration.client.execute(req).await?;
231
232    let status = resp.status();
233    let content_type = resp
234        .headers()
235        .get("content-type")
236        .and_then(|v| v.to_str().ok())
237        .unwrap_or("application/octet-stream");
238    let content_type = super::ContentType::from(content_type);
239
240    if !status.is_client_error() && !status.is_server_error() {
241        let content = resp.text().await?;
242        match content_type {
243            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
244            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DeviceView`"))),
245            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::DeviceView`")))),
246        }
247    } else {
248        let content = resp.text().await?;
249        let entity: Option<GetLinkDevicesByMachineError> = serde_json::from_str(&content).ok();
250        Err(Error::ResponseError(ResponseContent { status, content, entity }))
251    }
252}
253
254/// Gets the failover order across your linked accounts.  It answers an ordered redundancy plan over the caller's LINKED (not revoked) accounts: each candidate with its remaining rate-limit headroom, whether it is routable right now, how it BILLS (plan or commerce), and a reason when it is not — plus the primary to try first. It is what lets a router fail over from one subscription to another and fall back to the metered API as the always-available backstop, knowing the cost consequence before it dials.  It is POLICY, not execution: the plan is computed purely from the usage snapshots already in the registry, never by probing a provider, so it is a total function of the links and costs nothing to ask for. Actually dialing, detecting a live 429 and advancing to the next candidate belongs to the caller. A link with no snapshot counts as full headroom.
255pub async fn get_link_route(configuration: &configuration::Configuration, ) -> Result<models::RoutePlan, Error<GetLinkRouteError>> {
256
257    let uri_str = format!("{}/v1/link/route", configuration.base_path);
258    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
259
260    if let Some(ref user_agent) = configuration.user_agent {
261        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
262    }
263    if let Some(ref token) = configuration.bearer_access_token {
264        req_builder = req_builder.bearer_auth(token.to_owned());
265    };
266
267    let req = req_builder.build()?;
268    let resp = configuration.client.execute(req).await?;
269
270    let status = resp.status();
271    let content_type = resp
272        .headers()
273        .get("content-type")
274        .and_then(|v| v.to_str().ok())
275        .unwrap_or("application/octet-stream");
276    let content_type = super::ContentType::from(content_type);
277
278    if !status.is_client_error() && !status.is_server_error() {
279        let content = resp.text().await?;
280        match content_type {
281            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
282            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::RoutePlan`"))),
283            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::RoutePlan`")))),
284        }
285    } else {
286        let content = resp.text().await?;
287        let entity: Option<GetLinkRouteError> = serde_json::from_str(&content).ok();
288        Err(Error::ResponseError(ResponseContent { status, content, entity }))
289    }
290}
291
292/// Shows one provider account's own usage dashboard.  It answers the time series for a SINGLE provider account — the windows in range plus the currently-open ones — as that provider's own meter reported it: \"my plan is 47% through its 6h window, resets at 14:20\". current is the newest instance of each lane (the headline); windows is the history behind it, both computed from ONE deduped read. provider is required; an unknown window class or range is 400, never a quiet fallback to a different one. When no series is available the response is a 200 with available:false and empty lists — an honest \"we have no data\", which is a different claim from zero usage.
293pub async fn get_link_usage(configuration: &configuration::Configuration, provider: Option<&str>, account: Option<&str>, window: Option<&str>, range: Option<&str>) -> Result<models::BoardResp, Error<GetLinkUsageError>> {
294    // add a prefix to parameters to efficiently prevent name collisions
295    let p_provider = provider;
296    let p_account = account;
297    let p_window = window;
298    let p_range = range;
299
300    let uri_str = format!("{}/v1/link/usage", configuration.base_path);
301    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
302
303    if let Some(ref param_value) = p_provider {
304        req_builder = req_builder.query(&[("provider", &param_value.to_string())]);
305    }
306    if let Some(ref param_value) = p_account {
307        req_builder = req_builder.query(&[("account", &param_value.to_string())]);
308    }
309    if let Some(ref param_value) = p_window {
310        req_builder = req_builder.query(&[("window", &param_value.to_string())]);
311    }
312    if let Some(ref param_value) = p_range {
313        req_builder = req_builder.query(&[("range", &param_value.to_string())]);
314    }
315    if let Some(ref user_agent) = configuration.user_agent {
316        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
317    }
318    if let Some(ref token) = configuration.bearer_access_token {
319        req_builder = req_builder.bearer_auth(token.to_owned());
320    };
321
322    let req = req_builder.build()?;
323    let resp = configuration.client.execute(req).await?;
324
325    let status = resp.status();
326    let content_type = resp
327        .headers()
328        .get("content-type")
329        .and_then(|v| v.to_str().ok())
330        .unwrap_or("application/octet-stream");
331    let content_type = super::ContentType::from(content_type);
332
333    if !status.is_client_error() && !status.is_server_error() {
334        let content = resp.text().await?;
335        match content_type {
336            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
337            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::BoardResp`"))),
338            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::BoardResp`")))),
339        }
340    } else {
341        let content = resp.text().await?;
342        let entity: Option<GetLinkUsageError> = serde_json::from_str(&content).ok();
343        Err(Error::ResponseError(ResponseContent { status, content, entity }))
344    }
345}
346
347/// Breaks down what the gateway routed through each of your accounts.  It answers one row per linked account the GATEWAY actually routed through, plus their total — requests, prompt and completion tokens, and cost. This is the routed ledger, the read twin of the counter the router writes, and it is distinct from both of its neighbours: not the device collector's plan snapshots, and not the org money ledger. The source and scope fields on the response say so on every payload. The same shape answers in the billing namespace, from one shaping function, so the two mounts cannot drift.
348pub async fn get_link_usage_accounts(configuration: &configuration::Configuration, ) -> Result<models::AccountsUsage, Error<GetLinkUsageAccountsError>> {
349
350    let uri_str = format!("{}/v1/link/usage/accounts", configuration.base_path);
351    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
352
353    if let Some(ref user_agent) = configuration.user_agent {
354        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
355    }
356    if let Some(ref token) = configuration.bearer_access_token {
357        req_builder = req_builder.bearer_auth(token.to_owned());
358    };
359
360    let req = req_builder.build()?;
361    let resp = configuration.client.execute(req).await?;
362
363    let status = resp.status();
364    let content_type = resp
365        .headers()
366        .get("content-type")
367        .and_then(|v| v.to_str().ok())
368        .unwrap_or("application/octet-stream");
369    let content_type = super::ContentType::from(content_type);
370
371    if !status.is_client_error() && !status.is_server_error() {
372        let content = resp.text().await?;
373        match content_type {
374            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
375            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::AccountsUsage`"))),
376            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::AccountsUsage`")))),
377        }
378    } else {
379        let content = resp.text().await?;
380        let entity: Option<GetLinkUsageAccountsError> = serde_json::from_str(&content).ok();
381        Err(Error::ResponseError(ResponseContent { status, content, entity }))
382    }
383}
384
385/// Shows plan consumption and Hanzo spend side by side.  It answers the global usage board over one window: the caller's own linked accounts, metered from each provider's own login, alongside their org's Hanzo-routed inference. These come from different ledgers and mean different things, so every row is LABELLED by source, by scope and by availability, and THE TWO ARE NEVER SUMMED — a plan's percentage is not money, and a provider's own spend is not a Hanzo charge. The rows sit side by side and say what they are.  One resolver fixes the window for both halves, so the two sets always cover the same period. range is one of 1h, 24h, 7d or 30d and defaults to 24h; anything else is 400 rather than a silent substitution. A ledger that cannot answer reports available:false instead of a zero that would read as \"no usage\".
386pub async fn get_link_usage_summary(configuration: &configuration::Configuration, range: Option<&str>) -> Result<models::SummaryResp, Error<GetLinkUsageSummaryError>> {
387    // add a prefix to parameters to efficiently prevent name collisions
388    let p_range = range;
389
390    let uri_str = format!("{}/v1/link/usage/summary", configuration.base_path);
391    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
392
393    if let Some(ref param_value) = p_range {
394        req_builder = req_builder.query(&[("range", &param_value.to_string())]);
395    }
396    if let Some(ref user_agent) = configuration.user_agent {
397        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
398    }
399    if let Some(ref token) = configuration.bearer_access_token {
400        req_builder = req_builder.bearer_auth(token.to_owned());
401    };
402
403    let req = req_builder.build()?;
404    let resp = configuration.client.execute(req).await?;
405
406    let status = resp.status();
407    let content_type = resp
408        .headers()
409        .get("content-type")
410        .and_then(|v| v.to_str().ok())
411        .unwrap_or("application/octet-stream");
412    let content_type = super::ContentType::from(content_type);
413
414    if !status.is_client_error() && !status.is_server_error() {
415        let content = resp.text().await?;
416        match content_type {
417            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
418            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::SummaryResp`"))),
419            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::SummaryResp`")))),
420        }
421    } else {
422        let content = resp.text().await?;
423        let entity: Option<GetLinkUsageSummaryError> = serde_json::from_str(&content).ok();
424        Err(Error::ResponseError(ResponseContent { status, content, entity }))
425    }
426}
427
428/// Registers a signed-in AI provider account on a machine.  It records that a developer has signed into one provider account on one machine — a Claude Max or ChatGPT Plus subscription, a Hanzo key, a raw provider key — and answers 201 with the stored link. Re-reporting the same (machine, provider, account) UPDATES that link rather than creating a second, so a collector may call this on every heartbeat. machine and provider are required (400 otherwise), as is a valid kind, and every field is length-bounded. Scoped to the caller: a validated principal and a non-empty org, else 403, so a caller writes only their OWN accounts within their own org.
429pub async fn post_link(configuration: &configuration::Configuration, enroll_req: models::EnrollReq) -> Result<models::LinkView, Error<PostLinkError>> {
430    // add a prefix to parameters to efficiently prevent name collisions
431    let p_enroll_req = enroll_req;
432
433    let uri_str = format!("{}/v1/link", configuration.base_path);
434    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
435
436    if let Some(ref user_agent) = configuration.user_agent {
437        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
438    }
439    if let Some(ref token) = configuration.bearer_access_token {
440        req_builder = req_builder.bearer_auth(token.to_owned());
441    };
442    req_builder = req_builder.json(&p_enroll_req);
443
444    let req = req_builder.build()?;
445    let resp = configuration.client.execute(req).await?;
446
447    let status = resp.status();
448    let content_type = resp
449        .headers()
450        .get("content-type")
451        .and_then(|v| v.to_str().ok())
452        .unwrap_or("application/octet-stream");
453    let content_type = super::ContentType::from(content_type);
454
455    if !status.is_client_error() && !status.is_server_error() {
456        let content = resp.text().await?;
457        match content_type {
458            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
459            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::LinkView`"))),
460            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::LinkView`")))),
461        }
462    } else {
463        let content = resp.text().await?;
464        let entity: Option<PostLinkError> = serde_json::from_str(&content).ok();
465        Err(Error::ResponseError(ResponseContent { status, content, entity }))
466    }
467}
468
469/// Logs out every account on one machine and stops its sessions.  It revokes every one of the caller's accounts on one machine and stops the agent sessions they were running, answering with how many of each. This is the \"I lost that laptop\" button. Revoked links are RETAINED, not deleted, so usage history and the audit trail survive a log-out — the rows come back in the response with their new status. The session stop reaches only the REVOKING user's own sessions, so a shared machine name can never be used to stop a co-tenant's work, and a stop that fails does not fail the revoke: the revoked row is the durable truth and the count then honestly reports fewer. A machine with nothing left to revoke is 404.
470pub async fn post_link_devices_by_machine_revoke(configuration: &configuration::Configuration, machine: &str) -> Result<models::RevokeResp, Error<PostLinkDevicesByMachineRevokeError>> {
471    // add a prefix to parameters to efficiently prevent name collisions
472    let p_machine = machine;
473
474    let uri_str = format!("{}/v1/link/devices/{machine}/revoke", configuration.base_path, machine=crate::apis::urlencode(p_machine));
475    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
476
477    if let Some(ref user_agent) = configuration.user_agent {
478        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
479    }
480    if let Some(ref token) = configuration.bearer_access_token {
481        req_builder = req_builder.bearer_auth(token.to_owned());
482    };
483
484    let req = req_builder.build()?;
485    let resp = configuration.client.execute(req).await?;
486
487    let status = resp.status();
488    let content_type = resp
489        .headers()
490        .get("content-type")
491        .and_then(|v| v.to_str().ok())
492        .unwrap_or("application/octet-stream");
493    let content_type = super::ContentType::from(content_type);
494
495    if !status.is_client_error() && !status.is_server_error() {
496        let content = resp.text().await?;
497        match content_type {
498            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
499            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::RevokeResp`"))),
500            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::RevokeResp`")))),
501        }
502    } else {
503        let content = resp.text().await?;
504        let entity: Option<PostLinkDevicesByMachineRevokeError> = serde_json::from_str(&content).ok();
505        Err(Error::ResponseError(ResponseContent { status, content, entity }))
506    }
507}
508
509/// Reports usage samples from the device collector.  It ingests a batch of usage samples and answers with how many were accepted, whether history was durably stored, and the links they refreshed. A report also REFRESHES one link per distinct (machine, provider, account) it names, so a running collector keeps the accounts overview current without a separate registration call.  A caller can only ever report for THEMSELVES: org and subject come from the validated bearer, never from the body, so no sample can be attributed to another user or tenant. History is FAIL-SOFT and stored says which happened — a warehouse outage still accepts the report and refreshes the links rather than failing the device, and answers 202 either way. Send either one sample inline or up to 256 in samples; an empty batch or an over-long one is 400, as is a provider, window class or kind outside the closed vocabulary — an unrecognized window is refused rather than rewritten, because a silently reclassified sample would fill a dashboard with a class nobody reported.
510pub async fn post_link_usage(configuration: &configuration::Configuration, ingest_req: models::IngestReq) -> Result<models::IngestResp, Error<PostLinkUsageError>> {
511    // add a prefix to parameters to efficiently prevent name collisions
512    let p_ingest_req = ingest_req;
513
514    let uri_str = format!("{}/v1/link/usage", configuration.base_path);
515    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
516
517    if let Some(ref user_agent) = configuration.user_agent {
518        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
519    }
520    if let Some(ref token) = configuration.bearer_access_token {
521        req_builder = req_builder.bearer_auth(token.to_owned());
522    };
523    req_builder = req_builder.json(&p_ingest_req);
524
525    let req = req_builder.build()?;
526    let resp = configuration.client.execute(req).await?;
527
528    let status = resp.status();
529    let content_type = resp
530        .headers()
531        .get("content-type")
532        .and_then(|v| v.to_str().ok())
533        .unwrap_or("application/octet-stream");
534    let content_type = super::ContentType::from(content_type);
535
536    if !status.is_client_error() && !status.is_server_error() {
537        let content = resp.text().await?;
538        match content_type {
539            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
540            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::IngestResp`"))),
541            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::IngestResp`")))),
542        }
543    } else {
544        let content = resp.text().await?;
545        let entity: Option<PostLinkUsageError> = serde_json::from_str(&content).ok();
546        Err(Error::ResponseError(ResponseContent { status, content, entity }))
547    }
548}
549