Skip to main content

hanzo_client/apis/
books_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 [`get_books_accounts`]
19#[derive(Debug, Clone, Serialize, Deserialize)]
20#[serde(untagged)]
21pub enum GetBooksAccountsError {
22    UnknownValue(serde_json::Value),
23}
24
25/// struct for typed errors of method [`get_books_bank_transactions`]
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(untagged)]
28pub enum GetBooksBankTransactionsError {
29    UnknownValue(serde_json::Value),
30}
31
32/// struct for typed errors of method [`get_books_bank_unreconciled`]
33#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(untagged)]
35pub enum GetBooksBankUnreconciledError {
36    UnknownValue(serde_json::Value),
37}
38
39/// struct for typed errors of method [`get_books_export`]
40#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(untagged)]
42pub enum GetBooksExportError {
43    UnknownValue(serde_json::Value),
44}
45
46/// struct for typed errors of method [`get_books_gl`]
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(untagged)]
49pub enum GetBooksGlError {
50    UnknownValue(serde_json::Value),
51}
52
53/// struct for typed errors of method [`get_books_inbox`]
54#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(untagged)]
56pub enum GetBooksInboxError {
57    UnknownValue(serde_json::Value),
58}
59
60/// struct for typed errors of method [`get_books_metrics`]
61#[derive(Debug, Clone, Serialize, Deserialize)]
62#[serde(untagged)]
63pub enum GetBooksMetricsError {
64    UnknownValue(serde_json::Value),
65}
66
67/// struct for typed errors of method [`get_books_pnl`]
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(untagged)]
70pub enum GetBooksPnlError {
71    UnknownValue(serde_json::Value),
72}
73
74/// struct for typed errors of method [`get_books_position`]
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[serde(untagged)]
77pub enum GetBooksPositionError {
78    UnknownValue(serde_json::Value),
79}
80
81/// struct for typed errors of method [`get_books_questions`]
82#[derive(Debug, Clone, Serialize, Deserialize)]
83#[serde(untagged)]
84pub enum GetBooksQuestionsError {
85    UnknownValue(serde_json::Value),
86}
87
88/// struct for typed errors of method [`get_books_rules`]
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(untagged)]
91pub enum GetBooksRulesError {
92    UnknownValue(serde_json::Value),
93}
94
95/// struct for typed errors of method [`get_books_transactions`]
96#[derive(Debug, Clone, Serialize, Deserialize)]
97#[serde(untagged)]
98pub enum GetBooksTransactionsError {
99    UnknownValue(serde_json::Value),
100}
101
102/// struct for typed errors of method [`get_books_trial`]
103#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(untagged)]
105pub enum GetBooksTrialError {
106    UnknownValue(serde_json::Value),
107}
108
109/// struct for typed errors of method [`get_books_vendors`]
110#[derive(Debug, Clone, Serialize, Deserialize)]
111#[serde(untagged)]
112pub enum GetBooksVendorsError {
113    UnknownValue(serde_json::Value),
114}
115
116/// struct for typed errors of method [`post_books_ask`]
117#[derive(Debug, Clone, Serialize, Deserialize)]
118#[serde(untagged)]
119pub enum PostBooksAskError {
120    UnknownValue(serde_json::Value),
121}
122
123/// struct for typed errors of method [`post_books_bank_exchange`]
124#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(untagged)]
126pub enum PostBooksBankExchangeError {
127    UnknownValue(serde_json::Value),
128}
129
130/// struct for typed errors of method [`post_books_bank_import`]
131#[derive(Debug, Clone, Serialize, Deserialize)]
132#[serde(untagged)]
133pub enum PostBooksBankImportError {
134    UnknownValue(serde_json::Value),
135}
136
137/// struct for typed errors of method [`post_books_bank_sync`]
138#[derive(Debug, Clone, Serialize, Deserialize)]
139#[serde(untagged)]
140pub enum PostBooksBankSyncError {
141    UnknownValue(serde_json::Value),
142}
143
144/// struct for typed errors of method [`post_books_bank_token`]
145#[derive(Debug, Clone, Serialize, Deserialize)]
146#[serde(untagged)]
147pub enum PostBooksBankTokenError {
148    UnknownValue(serde_json::Value),
149}
150
151/// struct for typed errors of method [`post_books_inbox`]
152#[derive(Debug, Clone, Serialize, Deserialize)]
153#[serde(untagged)]
154pub enum PostBooksInboxError {
155    UnknownValue(serde_json::Value),
156}
157
158/// struct for typed errors of method [`post_books_rules`]
159#[derive(Debug, Clone, Serialize, Deserialize)]
160#[serde(untagged)]
161pub enum PostBooksRulesError {
162    UnknownValue(serde_json::Value),
163}
164
165/// struct for typed errors of method [`post_books_scan`]
166#[derive(Debug, Clone, Serialize, Deserialize)]
167#[serde(untagged)]
168pub enum PostBooksScanError {
169    UnknownValue(serde_json::Value),
170}
171
172/// struct for typed errors of method [`post_books_scan_book`]
173#[derive(Debug, Clone, Serialize, Deserialize)]
174#[serde(untagged)]
175pub enum PostBooksScanBookError {
176    UnknownValue(serde_json::Value),
177}
178
179/// struct for typed errors of method [`post_books_sync`]
180#[derive(Debug, Clone, Serialize, Deserialize)]
181#[serde(untagged)]
182pub enum PostBooksSyncError {
183    UnknownValue(serde_json::Value),
184}
185
186/// struct for typed errors of method [`post_books_vendors`]
187#[derive(Debug, Clone, Serialize, Deserialize)]
188#[serde(untagged)]
189pub enum PostBooksVendorsError {
190    UnknownValue(serde_json::Value),
191}
192
193
194/// Returns the org's chart of accounts — the seeded fixed chart every posting key in the ledger refers to.
195pub async fn get_books_accounts(configuration: &configuration::Configuration, sandbox: Option<&str>) -> Result<Vec<models::Account>, Error<GetBooksAccountsError>> {
196    // add a prefix to parameters to efficiently prevent name collisions
197    let p_sandbox = sandbox;
198
199    let uri_str = format!("{}/v1/books/accounts", configuration.base_path);
200    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
201
202    if let Some(ref param_value) = p_sandbox {
203        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
204    }
205    if let Some(ref user_agent) = configuration.user_agent {
206        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
207    }
208    if let Some(ref token) = configuration.bearer_access_token {
209        req_builder = req_builder.bearer_auth(token.to_owned());
210    };
211
212    let req = req_builder.build()?;
213    let resp = configuration.client.execute(req).await?;
214
215    let status = resp.status();
216    let content_type = resp
217        .headers()
218        .get("content-type")
219        .and_then(|v| v.to_str().ok())
220        .unwrap_or("application/octet-stream");
221    let content_type = super::ContentType::from(content_type);
222
223    if !status.is_client_error() && !status.is_server_error() {
224        let content = resp.text().await?;
225        match content_type {
226            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
227            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec&lt;models::Account&gt;`"))),
228            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec&lt;models::Account&gt;`")))),
229        }
230    } else {
231        let content = resp.text().await?;
232        let entity: Option<GetBooksAccountsError> = serde_json::from_str(&content).ok();
233        Err(Error::ResponseError(ResponseContent { status, content, entity }))
234    }
235}
236
237/// Returns the org's normalized bank transactions, newest first — every row the import and connector paths have ingested, with its amount in exact cents, its direction, and whether it has been matched to a voucher yet.
238pub async fn get_books_bank_transactions(configuration: &configuration::Configuration, sandbox: Option<&str>, limit: Option<i32>) -> Result<Vec<models::BankTxnRow>, Error<GetBooksBankTransactionsError>> {
239    // add a prefix to parameters to efficiently prevent name collisions
240    let p_sandbox = sandbox;
241    let p_limit = limit;
242
243    let uri_str = format!("{}/v1/books/bank/transactions", configuration.base_path);
244    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
245
246    if let Some(ref param_value) = p_sandbox {
247        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
248    }
249    if let Some(ref param_value) = p_limit {
250        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
251    }
252    if let Some(ref user_agent) = configuration.user_agent {
253        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
254    }
255    if let Some(ref token) = configuration.bearer_access_token {
256        req_builder = req_builder.bearer_auth(token.to_owned());
257    };
258
259    let req = req_builder.build()?;
260    let resp = configuration.client.execute(req).await?;
261
262    let status = resp.status();
263    let content_type = resp
264        .headers()
265        .get("content-type")
266        .and_then(|v| v.to_str().ok())
267        .unwrap_or("application/octet-stream");
268    let content_type = super::ContentType::from(content_type);
269
270    if !status.is_client_error() && !status.is_server_error() {
271        let content = resp.text().await?;
272        match content_type {
273            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
274            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec&lt;models::BankTxnRow&gt;`"))),
275            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec&lt;models::BankTxnRow&gt;`")))),
276        }
277    } else {
278        let content = resp.text().await?;
279        let entity: Option<GetBooksBankTransactionsError> = serde_json::from_str(&content).ok();
280        Err(Error::ResponseError(ResponseContent { status, content, entity }))
281    }
282}
283
284/// Returns the org's unmatched bank inflows and their open clarifying questions — the queue a human answers so an unexplained deposit is never guessed into revenue.
285pub async fn get_books_bank_unreconciled(configuration: &configuration::Configuration, sandbox: Option<&str>) -> Result<models::UnreconciledOut, Error<GetBooksBankUnreconciledError>> {
286    // add a prefix to parameters to efficiently prevent name collisions
287    let p_sandbox = sandbox;
288
289    let uri_str = format!("{}/v1/books/bank/unreconciled", configuration.base_path);
290    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
291
292    if let Some(ref param_value) = p_sandbox {
293        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
294    }
295    if let Some(ref user_agent) = configuration.user_agent {
296        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
297    }
298    if let Some(ref token) = configuration.bearer_access_token {
299        req_builder = req_builder.bearer_auth(token.to_owned());
300    };
301
302    let req = req_builder.build()?;
303    let resp = configuration.client.execute(req).await?;
304
305    let status = resp.status();
306    let content_type = resp
307        .headers()
308        .get("content-type")
309        .and_then(|v| v.to_str().ok())
310        .unwrap_or("application/octet-stream");
311    let content_type = super::ContentType::from(content_type);
312
313    if !status.is_client_error() && !status.is_server_error() {
314        let content = resp.text().await?;
315        match content_type {
316            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
317            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UnreconciledOut`"))),
318            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::UnreconciledOut`")))),
319        }
320    } else {
321        let content = resp.text().await?;
322        let entity: Option<GetBooksBankUnreconciledError> = serde_json::from_str(&content).ok();
323        Err(Error::ResponseError(ResponseContent { status, content, entity }))
324    }
325}
326
327/// Returns the complete financial package for the caller's org over (from, to]: the trial balance, the P&L, the balance sheet, and the GL detail behind them — the four statements a tax preparer or an investor asks for, assembled from the one ledger in a single read so they cannot disagree with each other.
328pub async fn get_books_export(configuration: &configuration::Configuration, sandbox: Option<&str>, from: Option<&str>, to: Option<&str>, format: Option<&str>, limit: Option<i32>) -> Result<models::FinancialPackage, Error<GetBooksExportError>> {
329    // add a prefix to parameters to efficiently prevent name collisions
330    let p_sandbox = sandbox;
331    let p_from = from;
332    let p_to = to;
333    let p_format = format;
334    let p_limit = limit;
335
336    let uri_str = format!("{}/v1/books/export", configuration.base_path);
337    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
338
339    if let Some(ref param_value) = p_sandbox {
340        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
341    }
342    if let Some(ref param_value) = p_from {
343        req_builder = req_builder.query(&[("from", &param_value.to_string())]);
344    }
345    if let Some(ref param_value) = p_to {
346        req_builder = req_builder.query(&[("to", &param_value.to_string())]);
347    }
348    if let Some(ref param_value) = p_format {
349        req_builder = req_builder.query(&[("format", &param_value.to_string())]);
350    }
351    if let Some(ref param_value) = p_limit {
352        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
353    }
354    if let Some(ref user_agent) = configuration.user_agent {
355        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
356    }
357    if let Some(ref token) = configuration.bearer_access_token {
358        req_builder = req_builder.bearer_auth(token.to_owned());
359    };
360
361    let req = req_builder.build()?;
362    let resp = configuration.client.execute(req).await?;
363
364    let status = resp.status();
365    let content_type = resp
366        .headers()
367        .get("content-type")
368        .and_then(|v| v.to_str().ok())
369        .unwrap_or("application/octet-stream");
370    let content_type = super::ContentType::from(content_type);
371
372    if !status.is_client_error() && !status.is_server_error() {
373        let content = resp.text().await?;
374        match content_type {
375            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
376            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::FinancialPackage`"))),
377            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::FinancialPackage`")))),
378        }
379    } else {
380        let content = resp.text().await?;
381        let entity: Option<GetBooksExportError> = serde_json::from_str(&content).ok();
382        Err(Error::ResponseError(ResponseContent { status, content, entity }))
383    }
384}
385
386/// ListGL returns the org's most recent GL Entry rows, newest first. This is the raw double-entry detail behind every statement: one row per leg, with its debit, credit, posting time and the source that booked it.
387pub async fn get_books_gl(configuration: &configuration::Configuration, sandbox: Option<&str>, limit: Option<i32>) -> Result<Vec<models::GlRow>, Error<GetBooksGlError>> {
388    // add a prefix to parameters to efficiently prevent name collisions
389    let p_sandbox = sandbox;
390    let p_limit = limit;
391
392    let uri_str = format!("{}/v1/books/gl", configuration.base_path);
393    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
394
395    if let Some(ref param_value) = p_sandbox {
396        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
397    }
398    if let Some(ref param_value) = p_limit {
399        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
400    }
401    if let Some(ref user_agent) = configuration.user_agent {
402        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
403    }
404    if let Some(ref token) = configuration.bearer_access_token {
405        req_builder = req_builder.bearer_auth(token.to_owned());
406    };
407
408    let req = req_builder.build()?;
409    let resp = configuration.client.execute(req).await?;
410
411    let status = resp.status();
412    let content_type = resp
413        .headers()
414        .get("content-type")
415        .and_then(|v| v.to_str().ok())
416        .unwrap_or("application/octet-stream");
417    let content_type = super::ContentType::from(content_type);
418
419    if !status.is_client_error() && !status.is_server_error() {
420        let content = resp.text().await?;
421        match content_type {
422            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
423            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `Vec&lt;models::GlRow&gt;`"))),
424            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `Vec&lt;models::GlRow&gt;`")))),
425        }
426    } else {
427        let content = resp.text().await?;
428        let entity: Option<GetBooksGlError> = serde_json::from_str(&content).ok();
429        Err(Error::ResponseError(ResponseContent { status, content, entity }))
430    }
431}
432
433/// Returns the org's open document queue — everything uploaded but not yet booked, newest first, each with its extracted summary and the confidence the scanner resolved its category at. A booked document drops out of the queue.
434pub async fn get_books_inbox(configuration: &configuration::Configuration, sandbox: Option<&str>) -> Result<models::InboxOut, Error<GetBooksInboxError>> {
435    // add a prefix to parameters to efficiently prevent name collisions
436    let p_sandbox = sandbox;
437
438    let uri_str = format!("{}/v1/books/inbox", configuration.base_path);
439    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
440
441    if let Some(ref param_value) = p_sandbox {
442        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
443    }
444    if let Some(ref user_agent) = configuration.user_agent {
445        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
446    }
447    if let Some(ref token) = configuration.bearer_access_token {
448        req_builder = req_builder.bearer_auth(token.to_owned());
449    };
450
451    let req = req_builder.build()?;
452    let resp = configuration.client.execute(req).await?;
453
454    let status = resp.status();
455    let content_type = resp
456        .headers()
457        .get("content-type")
458        .and_then(|v| v.to_str().ok())
459        .unwrap_or("application/octet-stream");
460    let content_type = super::ContentType::from(content_type);
461
462    if !status.is_client_error() && !status.is_server_error() {
463        let content = resp.text().await?;
464        match content_type {
465            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
466            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::InboxOut`"))),
467            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::InboxOut`")))),
468        }
469    } else {
470        let content = resp.text().await?;
471        let entity: Option<GetBooksInboxError> = serde_json::from_str(&content).ok();
472        Err(Error::ResponseError(ResponseContent { status, content, entity }))
473    }
474}
475
476/// Metrics returns the org's deterministic SaaS-metrics snapshot over an optional (from, to] window — MRR, ARR, revenue, COGS, burn, gross margin, net income, cash, deferred revenue, monthly burn and runway — as raw int64-cent figures AND the same figures already formatted. Every number is the ledger, aggregated the one way the books define it, never a guess; it is the grounded read the unified /v1/ask advisor replays.
477pub async fn get_books_metrics(configuration: &configuration::Configuration, sandbox: Option<&str>, from: Option<&str>, to: Option<&str>) -> Result<models::MetricsResponse, Error<GetBooksMetricsError>> {
478    // add a prefix to parameters to efficiently prevent name collisions
479    let p_sandbox = sandbox;
480    let p_from = from;
481    let p_to = to;
482
483    let uri_str = format!("{}/v1/books/metrics", configuration.base_path);
484    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
485
486    if let Some(ref param_value) = p_sandbox {
487        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
488    }
489    if let Some(ref param_value) = p_from {
490        req_builder = req_builder.query(&[("from", &param_value.to_string())]);
491    }
492    if let Some(ref param_value) = p_to {
493        req_builder = req_builder.query(&[("to", &param_value.to_string())]);
494    }
495    if let Some(ref user_agent) = configuration.user_agent {
496        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
497    }
498    if let Some(ref token) = configuration.bearer_access_token {
499        req_builder = req_builder.bearer_auth(token.to_owned());
500    };
501
502    let req = req_builder.build()?;
503    let resp = configuration.client.execute(req).await?;
504
505    let status = resp.status();
506    let content_type = resp
507        .headers()
508        .get("content-type")
509        .and_then(|v| v.to_str().ok())
510        .unwrap_or("application/octet-stream");
511    let content_type = super::ContentType::from(content_type);
512
513    if !status.is_client_error() && !status.is_server_error() {
514        let content = resp.text().await?;
515        match content_type {
516            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
517            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::MetricsResponse`"))),
518            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::MetricsResponse`")))),
519        }
520    } else {
521        let content = resp.text().await?;
522        let entity: Option<GetBooksMetricsError> = serde_json::from_str(&content).ok();
523        Err(Error::ResponseError(ResponseContent { status, content, entity }))
524    }
525}
526
527/// Returns the org's accrual-basis Profit & Loss over an optional (from, to] window of RFC3339 posting times: recognized revenue, matched cost, and the net.
528pub async fn get_books_pnl(configuration: &configuration::Configuration, sandbox: Option<&str>, from: Option<&str>, to: Option<&str>) -> Result<models::PnL, Error<GetBooksPnlError>> {
529    // add a prefix to parameters to efficiently prevent name collisions
530    let p_sandbox = sandbox;
531    let p_from = from;
532    let p_to = to;
533
534    let uri_str = format!("{}/v1/books/pnl", configuration.base_path);
535    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
536
537    if let Some(ref param_value) = p_sandbox {
538        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
539    }
540    if let Some(ref param_value) = p_from {
541        req_builder = req_builder.query(&[("from", &param_value.to_string())]);
542    }
543    if let Some(ref param_value) = p_to {
544        req_builder = req_builder.query(&[("to", &param_value.to_string())]);
545    }
546    if let Some(ref user_agent) = configuration.user_agent {
547        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
548    }
549    if let Some(ref token) = configuration.bearer_access_token {
550        req_builder = req_builder.bearer_auth(token.to_owned());
551    };
552
553    let req = req_builder.build()?;
554    let resp = configuration.client.execute(req).await?;
555
556    let status = resp.status();
557    let content_type = resp
558        .headers()
559        .get("content-type")
560        .and_then(|v| v.to_str().ok())
561        .unwrap_or("application/octet-stream");
562    let content_type = super::ContentType::from(content_type);
563
564    if !status.is_client_error() && !status.is_server_error() {
565        let content = resp.text().await?;
566        match content_type {
567            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
568            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::PnL`"))),
569            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::PnL`")))),
570        }
571    } else {
572        let content = resp.text().await?;
573        let entity: Option<GetBooksPnlError> = serde_json::from_str(&content).ok();
574        Err(Error::ResponseError(ResponseContent { status, content, entity }))
575    }
576}
577
578/// Returns the org's Balance Sheet as of `to` (empty = all time), with the Assets == Liabilities + Equity equation proof.
579pub async fn get_books_position(configuration: &configuration::Configuration, sandbox: Option<&str>, to: Option<&str>) -> Result<models::BalanceSheet, Error<GetBooksPositionError>> {
580    // add a prefix to parameters to efficiently prevent name collisions
581    let p_sandbox = sandbox;
582    let p_to = to;
583
584    let uri_str = format!("{}/v1/books/position", configuration.base_path);
585    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
586
587    if let Some(ref param_value) = p_sandbox {
588        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
589    }
590    if let Some(ref param_value) = p_to {
591        req_builder = req_builder.query(&[("to", &param_value.to_string())]);
592    }
593    if let Some(ref user_agent) = configuration.user_agent {
594        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
595    }
596    if let Some(ref token) = configuration.bearer_access_token {
597        req_builder = req_builder.bearer_auth(token.to_owned());
598    };
599
600    let req = req_builder.build()?;
601    let resp = configuration.client.execute(req).await?;
602
603    let status = resp.status();
604    let content_type = resp
605        .headers()
606        .get("content-type")
607        .and_then(|v| v.to_str().ok())
608        .unwrap_or("application/octet-stream");
609    let content_type = super::ContentType::from(content_type);
610
611    if !status.is_client_error() && !status.is_server_error() {
612        let content = resp.text().await?;
613        match content_type {
614            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
615            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::BalanceSheet`"))),
616            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::BalanceSheet`")))),
617        }
618    } else {
619        let content = resp.text().await?;
620        let entity: Option<GetBooksPositionError> = serde_json::from_str(&content).ok();
621        Err(Error::ResponseError(ResponseContent { status, content, entity }))
622    }
623}
624
625/// Returns the clarifying questions the caller's own recent GL raises — the unusual postings a founder should look at (outliers, reversals, round-offs, uncosted revenue, an overdrawn wallet), sharpest first. An empty list means the books look clean; the detector is deterministic over the ledger and invents nothing.
626pub async fn get_books_questions(configuration: &configuration::Configuration, sandbox: Option<&str>) -> Result<models::QuestionsResponse, Error<GetBooksQuestionsError>> {
627    // add a prefix to parameters to efficiently prevent name collisions
628    let p_sandbox = sandbox;
629
630    let uri_str = format!("{}/v1/books/questions", configuration.base_path);
631    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
632
633    if let Some(ref param_value) = p_sandbox {
634        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
635    }
636    if let Some(ref user_agent) = configuration.user_agent {
637        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
638    }
639    if let Some(ref token) = configuration.bearer_access_token {
640        req_builder = req_builder.bearer_auth(token.to_owned());
641    };
642
643    let req = req_builder.build()?;
644    let resp = configuration.client.execute(req).await?;
645
646    let status = resp.status();
647    let content_type = resp
648        .headers()
649        .get("content-type")
650        .and_then(|v| v.to_str().ok())
651        .unwrap_or("application/octet-stream");
652    let content_type = super::ContentType::from(content_type);
653
654    if !status.is_client_error() && !status.is_server_error() {
655        let content = resp.text().await?;
656        match content_type {
657            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
658            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::QuestionsResponse`"))),
659            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::QuestionsResponse`")))),
660        }
661    } else {
662        let content = resp.text().await?;
663        let entity: Option<GetBooksQuestionsError> = serde_json::from_str(&content).ok();
664        Err(Error::ResponseError(ResponseContent { status, content, entity }))
665    }
666}
667
668/// Returns the org's auto-categorization rules, highest priority first. A rule is a standing instruction — \"anything whose merchant contains X books to category Y\" — and it overrides a vendor's default category, so this is the list that decides how a future bill classifies itself.
669pub async fn get_books_rules(configuration: &configuration::Configuration, sandbox: Option<&str>) -> Result<models::RulesOut, Error<GetBooksRulesError>> {
670    // add a prefix to parameters to efficiently prevent name collisions
671    let p_sandbox = sandbox;
672
673    let uri_str = format!("{}/v1/books/rules", configuration.base_path);
674    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
675
676    if let Some(ref param_value) = p_sandbox {
677        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
678    }
679    if let Some(ref user_agent) = configuration.user_agent {
680        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
681    }
682    if let Some(ref token) = configuration.bearer_access_token {
683        req_builder = req_builder.bearer_auth(token.to_owned());
684    };
685
686    let req = req_builder.build()?;
687    let resp = configuration.client.execute(req).await?;
688
689    let status = resp.status();
690    let content_type = resp
691        .headers()
692        .get("content-type")
693        .and_then(|v| v.to_str().ok())
694        .unwrap_or("application/octet-stream");
695    let content_type = super::ContentType::from(content_type);
696
697    if !status.is_client_error() && !status.is_server_error() {
698        let content = resp.text().await?;
699        match content_type {
700            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
701            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::RulesOut`"))),
702            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::RulesOut`")))),
703        }
704    } else {
705        let content = resp.text().await?;
706        let entity: Option<GetBooksRulesError> = serde_json::from_str(&content).ok();
707        Err(Error::ResponseError(ResponseContent { status, content, entity }))
708    }
709}
710
711/// Returns the org's booked ledger as a single-line register, newest first: one row per voucher, with its date, description, vendor, category, source and amount in exact cents. It is the double-entry ledger projected to the register a human reads, filterable by posting-time window, category and vendor. Strictly read-only — it restates the books, it never moves them.
712pub async fn get_books_transactions(configuration: &configuration::Configuration, sandbox: Option<&str>, from: Option<&str>, to: Option<&str>, category: Option<&str>, vendor: Option<&str>, limit: Option<i32>) -> Result<models::TransactionsOut, Error<GetBooksTransactionsError>> {
713    // add a prefix to parameters to efficiently prevent name collisions
714    let p_sandbox = sandbox;
715    let p_from = from;
716    let p_to = to;
717    let p_category = category;
718    let p_vendor = vendor;
719    let p_limit = limit;
720
721    let uri_str = format!("{}/v1/books/transactions", configuration.base_path);
722    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
723
724    if let Some(ref param_value) = p_sandbox {
725        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
726    }
727    if let Some(ref param_value) = p_from {
728        req_builder = req_builder.query(&[("from", &param_value.to_string())]);
729    }
730    if let Some(ref param_value) = p_to {
731        req_builder = req_builder.query(&[("to", &param_value.to_string())]);
732    }
733    if let Some(ref param_value) = p_category {
734        req_builder = req_builder.query(&[("category", &param_value.to_string())]);
735    }
736    if let Some(ref param_value) = p_vendor {
737        req_builder = req_builder.query(&[("vendor", &param_value.to_string())]);
738    }
739    if let Some(ref param_value) = p_limit {
740        req_builder = req_builder.query(&[("limit", &param_value.to_string())]);
741    }
742    if let Some(ref user_agent) = configuration.user_agent {
743        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
744    }
745    if let Some(ref token) = configuration.bearer_access_token {
746        req_builder = req_builder.bearer_auth(token.to_owned());
747    };
748
749    let req = req_builder.build()?;
750    let resp = configuration.client.execute(req).await?;
751
752    let status = resp.status();
753    let content_type = resp
754        .headers()
755        .get("content-type")
756        .and_then(|v| v.to_str().ok())
757        .unwrap_or("application/octet-stream");
758    let content_type = super::ContentType::from(content_type);
759
760    if !status.is_client_error() && !status.is_server_error() {
761        let content = resp.text().await?;
762        match content_type {
763            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
764            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::TransactionsOut`"))),
765            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::TransactionsOut`")))),
766        }
767    } else {
768        let content = resp.text().await?;
769        let entity: Option<GetBooksTransactionsError> = serde_json::from_str(&content).ok();
770        Err(Error::ResponseError(ResponseContent { status, content, entity }))
771    }
772}
773
774/// Returns the org's trial balance over an optional [from, to] window of RFC3339 posting times, including the opening/closing columns and the TotalDebit == TotalCredit proof that the books balance.
775pub async fn get_books_trial(configuration: &configuration::Configuration, sandbox: Option<&str>, from: Option<&str>, to: Option<&str>) -> Result<models::TrialBalance, Error<GetBooksTrialError>> {
776    // add a prefix to parameters to efficiently prevent name collisions
777    let p_sandbox = sandbox;
778    let p_from = from;
779    let p_to = to;
780
781    let uri_str = format!("{}/v1/books/trial", configuration.base_path);
782    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
783
784    if let Some(ref param_value) = p_sandbox {
785        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
786    }
787    if let Some(ref param_value) = p_from {
788        req_builder = req_builder.query(&[("from", &param_value.to_string())]);
789    }
790    if let Some(ref param_value) = p_to {
791        req_builder = req_builder.query(&[("to", &param_value.to_string())]);
792    }
793    if let Some(ref user_agent) = configuration.user_agent {
794        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
795    }
796    if let Some(ref token) = configuration.bearer_access_token {
797        req_builder = req_builder.bearer_auth(token.to_owned());
798    };
799
800    let req = req_builder.build()?;
801    let resp = configuration.client.execute(req).await?;
802
803    let status = resp.status();
804    let content_type = resp
805        .headers()
806        .get("content-type")
807        .and_then(|v| v.to_str().ok())
808        .unwrap_or("application/octet-stream");
809    let content_type = super::ContentType::from(content_type);
810
811    if !status.is_client_error() && !status.is_server_error() {
812        let content = resp.text().await?;
813        match content_type {
814            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
815            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::TrialBalance`"))),
816            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::TrialBalance`")))),
817        }
818    } else {
819        let content = resp.text().await?;
820        let entity: Option<GetBooksTrialError> = serde_json::from_str(&content).ok();
821        Err(Error::ResponseError(ResponseContent { status, content, entity }))
822    }
823}
824
825/// Returns the org's vendor book: each canonical vendor, the alias spellings a receipt may print it under, and the expense account new bills from it default to. A vendor here is what makes a scanned bill self-classify instead of asking again.
826pub async fn get_books_vendors(configuration: &configuration::Configuration, sandbox: Option<&str>) -> Result<models::VendorsOut, Error<GetBooksVendorsError>> {
827    // add a prefix to parameters to efficiently prevent name collisions
828    let p_sandbox = sandbox;
829
830    let uri_str = format!("{}/v1/books/vendors", configuration.base_path);
831    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
832
833    if let Some(ref param_value) = p_sandbox {
834        req_builder = req_builder.query(&[("sandbox", &param_value.to_string())]);
835    }
836    if let Some(ref user_agent) = configuration.user_agent {
837        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
838    }
839    if let Some(ref token) = configuration.bearer_access_token {
840        req_builder = req_builder.bearer_auth(token.to_owned());
841    };
842
843    let req = req_builder.build()?;
844    let resp = configuration.client.execute(req).await?;
845
846    let status = resp.status();
847    let content_type = resp
848        .headers()
849        .get("content-type")
850        .and_then(|v| v.to_str().ok())
851        .unwrap_or("application/octet-stream");
852    let content_type = super::ContentType::from(content_type);
853
854    if !status.is_client_error() && !status.is_server_error() {
855        let content = resp.text().await?;
856        match content_type {
857            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
858            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::VendorsOut`"))),
859            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::VendorsOut`")))),
860        }
861    } else {
862        let content = resp.text().await?;
863        let entity: Option<GetBooksVendorsError> = serde_json::from_str(&content).ok();
864        Err(Error::ResponseError(ResponseContent { status, content, entity }))
865    }
866}
867
868/// Answers a plain-language question about the caller's own books — \"what is my MRR?\", \"how long is my runway?\" — with figures taken from their ledger, never a guessed number. A deterministic keyword router picks the intent and reads the real metrics, and those figures, followups and report sources are computed BEFORE any model call and are never altered by one: the optional narration client only rephrases the sentence, and it degrades silently to the templated answer when no AI plane is wired. It is strictly read-only — it restates the books, it never posts to them.
869pub async fn post_books_ask(configuration: &configuration::Configuration, ask_request: models::AskRequest) -> Result<models::AskResponse, Error<PostBooksAskError>> {
870    // add a prefix to parameters to efficiently prevent name collisions
871    let p_ask_request = ask_request;
872
873    let uri_str = format!("{}/v1/books/ask", configuration.base_path);
874    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
875
876    if let Some(ref user_agent) = configuration.user_agent {
877        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
878    }
879    if let Some(ref token) = configuration.bearer_access_token {
880        req_builder = req_builder.bearer_auth(token.to_owned());
881    };
882    req_builder = req_builder.json(&p_ask_request);
883
884    let req = req_builder.build()?;
885    let resp = configuration.client.execute(req).await?;
886
887    let status = resp.status();
888    let content_type = resp
889        .headers()
890        .get("content-type")
891        .and_then(|v| v.to_str().ok())
892        .unwrap_or("application/octet-stream");
893    let content_type = super::ContentType::from(content_type);
894
895    if !status.is_client_error() && !status.is_server_error() {
896        let content = resp.text().await?;
897        match content_type {
898            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
899            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::AskResponse`"))),
900            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::AskResponse`")))),
901        }
902    } else {
903        let content = resp.text().await?;
904        let entity: Option<PostBooksAskError> = serde_json::from_str(&content).ok();
905        Err(Error::ResponseError(ResponseContent { status, content, entity }))
906    }
907}
908
909/// ANSWERS 501 UNCONDITIONALLY. It is the intended second hop of the bank-linking handshake — trade the provider's short-lived public token for the durable access credential and seal that credential into KMS — and nothing on the HTTP path reaches an implementation today.  The durable bank credential is the reason this hop exists: it is meant to be sealed server-side and never handed back to the caller. Since the route never succeeds, no credential is stored by it and no bank is connected through it.  Documented as refusing rather than declared with a success body, for the same reason as the first hop: it has never sent one, and stating a shape it has never produced would put a return type in every SDK for a call that always fails. A caller with no principal gets 401 before the 501.
910pub async fn post_books_bank_exchange(configuration: &configuration::Configuration, ) -> Result<(), Error<PostBooksBankExchangeError>> {
911
912    let uri_str = format!("{}/v1/books/bank/exchange", configuration.base_path);
913    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
914
915    if let Some(ref user_agent) = configuration.user_agent {
916        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
917    }
918    if let Some(ref token) = configuration.bearer_access_token {
919        req_builder = req_builder.bearer_auth(token.to_owned());
920    };
921
922    let req = req_builder.build()?;
923    let resp = configuration.client.execute(req).await?;
924
925    let status = resp.status();
926
927    if !status.is_client_error() && !status.is_server_error() {
928        Ok(())
929    } else {
930        let content = resp.text().await?;
931        let entity: Option<PostBooksBankExchangeError> = serde_json::from_str(&content).ok();
932        Err(Error::ResponseError(ResponseContent { status, content, entity }))
933    }
934}
935
936/// Takes a bank statement as RAW BYTES — the file exactly as downloaded, OFX, QFX or CSV, not wrapped in JSON — parses every row, books it against the caller org's own ledger, and answers the tally: how many rows were seen, how many vouchers posted, how many inflows reconciled, how many raised a question, how many were own-account transfers, and how many were skipped.  RE-IMPORTING THE SAME STATEMENT DOES NOT DOUBLE-BOOK. Every row goes through the same posting choke point every other source uses, keyed idempotently, so an overlapping statement — the usual case, since exports overlap at the month boundary — lands its new rows and counts the rest as skipped. Skipped is the number to read on a second import.  It is READ-ONLY against the bank: this ingests, it never sends money. Scoped to the caller's own org from the validated principal, and refused without one; `sandbox=true` writes the org's sandbox ledger instead of its real books. An empty body is a 400, and a file the parser cannot read is a 400 carrying the parser's reason rather than a partial import. On a deployment whose import parser is not built, this answers 501 rather than mishandling the file.
937pub async fn post_books_bank_import(configuration: &configuration::Configuration, body: Option<Vec<u8>>) -> Result<models::BankTally, Error<PostBooksBankImportError>> {
938    // add a prefix to parameters to efficiently prevent name collisions
939    let p_body = body;
940
941    let uri_str = format!("{}/v1/books/bank/import", configuration.base_path);
942    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
943
944    if let Some(ref user_agent) = configuration.user_agent {
945        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
946    }
947    if let Some(ref token) = configuration.bearer_access_token {
948        req_builder = req_builder.bearer_auth(token.to_owned());
949    };
950    if let Some(body) = p_body {
951        req_builder = req_builder.body(body);
952    }
953
954    let req = req_builder.build()?;
955    let resp = configuration.client.execute(req).await?;
956
957    let status = resp.status();
958    let content_type = resp
959        .headers()
960        .get("content-type")
961        .and_then(|v| v.to_str().ok())
962        .unwrap_or("application/octet-stream");
963    let content_type = super::ContentType::from(content_type);
964
965    if !status.is_client_error() && !status.is_server_error() {
966        let content = resp.text().await?;
967        match content_type {
968            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
969            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::BankTally`"))),
970            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::BankTally`")))),
971        }
972    } else {
973        let content = resp.text().await?;
974        let entity: Option<PostBooksBankImportError> = serde_json::from_str(&content).ok();
975        Err(Error::ResponseError(ResponseContent { status, content, entity }))
976    }
977}
978
979/// Pulls every connected bank (Plaid/Teller) for the caller's org, maps each fetched transaction to a posting and books it idempotently, then advances that connector's cursor so the next sync resumes where this one stopped. One connector's outage is skipped rather than failing the whole sync. It reports the batch: how many transactions were seen, how many vouchers posted, how many inflows reconciled against the processor clearing account, how many raised a question, how many were own-account transfers, and how many were already-processed no-ops. It is READ-ONLY against the bank — it ingests, it never sends money.
980pub async fn post_books_bank_sync(configuration: &configuration::Configuration, ) -> Result<models::BankTally, Error<PostBooksBankSyncError>> {
981
982    let uri_str = format!("{}/v1/books/bank/sync", configuration.base_path);
983    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
984
985    if let Some(ref user_agent) = configuration.user_agent {
986        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
987    }
988    if let Some(ref token) = configuration.bearer_access_token {
989        req_builder = req_builder.bearer_auth(token.to_owned());
990    };
991
992    let req = req_builder.build()?;
993    let resp = configuration.client.execute(req).await?;
994
995    let status = resp.status();
996    let content_type = resp
997        .headers()
998        .get("content-type")
999        .and_then(|v| v.to_str().ok())
1000        .unwrap_or("application/octet-stream");
1001    let content_type = super::ContentType::from(content_type);
1002
1003    if !status.is_client_error() && !status.is_server_error() {
1004        let content = resp.text().await?;
1005        match content_type {
1006            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
1007            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::BankTally`"))),
1008            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::BankTally`")))),
1009        }
1010    } else {
1011        let content = resp.text().await?;
1012        let entity: Option<PostBooksBankSyncError> = serde_json::from_str(&content).ok();
1013        Err(Error::ResponseError(ResponseContent { status, content, entity }))
1014    }
1015}
1016
1017/// ANSWERS 501 UNCONDITIONALLY. It is the intended first hop of the bank-linking handshake — mint the short-lived session token a browser hands to the provider's link widget — and nothing on the HTTP path reaches an implementation today.  The connectors behind it are written and tested; only the wiring is missing, so an org cannot connect a bank through the API at all. Until that lands, bank data reaches the books by statement import.  It is documented as refusing rather than declared with a success body precisely because it has never sent one. A response schema here would be invention: every generated SDK would carry a return type for a call that has only ever failed. A caller with no principal gets 401 before the 501.
1018pub async fn post_books_bank_token(configuration: &configuration::Configuration, ) -> Result<(), Error<PostBooksBankTokenError>> {
1019
1020    let uri_str = format!("{}/v1/books/bank/token", configuration.base_path);
1021    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
1022
1023    if let Some(ref user_agent) = configuration.user_agent {
1024        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
1025    }
1026    if let Some(ref token) = configuration.bearer_access_token {
1027        req_builder = req_builder.bearer_auth(token.to_owned());
1028    };
1029
1030    let req = req_builder.build()?;
1031    let resp = configuration.client.execute(req).await?;
1032
1033    let status = resp.status();
1034
1035    if !status.is_client_error() && !status.is_server_error() {
1036        Ok(())
1037    } else {
1038        let content = resp.text().await?;
1039        let entity: Option<PostBooksBankTokenError> = serde_json::from_str(&content).ok();
1040        Err(Error::ResponseError(ResponseContent { status, content, entity }))
1041    }
1042}
1043
1044/// Takes a document as RAW BYTES and queues it in the caller org's inbox as `unsorted`, answering the queued item. It is the drop box: get the paperwork in now, read it later.  It EXTRACTS NOTHING and calls no model — that is what separates it from the scan. Nothing is proposed and nothing is posted; the item simply waits to be scanned, and a booked document leaves the queue.  IDEMPOTENT BY CONTENT: the item's id is the file hash, so re-uploading the same bytes answers the existing item rather than adding a duplicate row — and it is the same id a scan of those bytes uses, which is how the two routes address one document. Scoped to the caller's own org from the validated principal and refused without one; `sandbox=true` targets the sandbox ledger, and `filename` is recorded for display. An empty or oversized upload is a 400.
1045pub async fn post_books_inbox(configuration: &configuration::Configuration, body: Option<Vec<u8>>) -> Result<models::InboxItem, Error<PostBooksInboxError>> {
1046    // add a prefix to parameters to efficiently prevent name collisions
1047    let p_body = body;
1048
1049    let uri_str = format!("{}/v1/books/inbox", configuration.base_path);
1050    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
1051
1052    if let Some(ref user_agent) = configuration.user_agent {
1053        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
1054    }
1055    if let Some(ref token) = configuration.bearer_access_token {
1056        req_builder = req_builder.bearer_auth(token.to_owned());
1057    };
1058    if let Some(body) = p_body {
1059        req_builder = req_builder.body(body);
1060    }
1061
1062    let req = req_builder.build()?;
1063    let resp = configuration.client.execute(req).await?;
1064
1065    let status = resp.status();
1066    let content_type = resp
1067        .headers()
1068        .get("content-type")
1069        .and_then(|v| v.to_str().ok())
1070        .unwrap_or("application/octet-stream");
1071    let content_type = super::ContentType::from(content_type);
1072
1073    if !status.is_client_error() && !status.is_server_error() {
1074        let content = resp.text().await?;
1075        match content_type {
1076            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
1077            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::InboxItem`"))),
1078            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::InboxItem`")))),
1079        }
1080    } else {
1081        let content = resp.text().await?;
1082        let entity: Option<PostBooksInboxError> = serde_json::from_str(&content).ok();
1083        Err(Error::ResponseError(ResponseContent { status, content, entity }))
1084    }
1085}
1086
1087/// Creates or updates one auto-categorization rule, keyed by its pattern — writing a pattern that already exists REPLACES that row's category and priority. The category is normalized to a real COA expense account, and anything unrecognized becomes 5900 Uncategorized rather than a guessed real account. It answers the row exactly as stored, so the caller sees the normalization. A rule overrides a vendor's default category, so this is the standing instruction that decides how a future bill classifies.
1088pub async fn post_books_rules(configuration: &configuration::Configuration, rule: models::Rule) -> Result<models::Rule, Error<PostBooksRulesError>> {
1089    // add a prefix to parameters to efficiently prevent name collisions
1090    let p_rule = rule;
1091
1092    let uri_str = format!("{}/v1/books/rules", configuration.base_path);
1093    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
1094
1095    if let Some(ref user_agent) = configuration.user_agent {
1096        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
1097    }
1098    if let Some(ref token) = configuration.bearer_access_token {
1099        req_builder = req_builder.bearer_auth(token.to_owned());
1100    };
1101    req_builder = req_builder.json(&p_rule);
1102
1103    let req = req_builder.build()?;
1104    let resp = configuration.client.execute(req).await?;
1105
1106    let status = resp.status();
1107    let content_type = resp
1108        .headers()
1109        .get("content-type")
1110        .and_then(|v| v.to_str().ok())
1111        .unwrap_or("application/octet-stream");
1112    let content_type = super::ContentType::from(content_type);
1113
1114    if !status.is_client_error() && !status.is_server_error() {
1115        let content = resp.text().await?;
1116        match content_type {
1117            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
1118            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Rule`"))),
1119            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::Rule`")))),
1120        }
1121    } else {
1122        let content = resp.text().await?;
1123        let entity: Option<PostBooksRulesError> = serde_json::from_str(&content).ok();
1124        Err(Error::ResponseError(ResponseContent { status, content, entity }))
1125    }
1126}
1127
1128/// Takes a receipt or invoice as RAW BYTES — a PDF, an image or plain text, uploaded under its own content type, not wrapped in JSON — extracts what the document says, resolves the vendor's expense category, and answers a DRAFT carrying a balanced voucher proposed for it.  NOTHING IS POSTED. That split is the whole design: the model only ever produces a structured reading of the document, the voucher is assembled deterministically in Go from that reading, and the ledger is written only by the separate book call a human confirms. So a misread scan can propose a wrong draft; it cannot move money. Amounts are exact integer cents end to end — the extraction returns cents, never a decimal — so no rounding enters the ledger.  The draft's id is the FILE HASH, and that is what makes booking idempotent: re-scanning the same bytes addresses the same draft rather than queuing a second one. A row is written to the org's document inbox as a side effect, moving it from unsorted to draft. Scoped to the caller's own org from the validated principal and refused without one; `sandbox=true` targets the sandbox ledger, and `filename` is recorded for the inbox. An empty or oversized upload is a 400, and a deployment with no scanner model answers 501.
1129pub async fn post_books_scan(configuration: &configuration::Configuration, body: Option<Vec<u8>>) -> Result<models::ScanDraft, Error<PostBooksScanError>> {
1130    // add a prefix to parameters to efficiently prevent name collisions
1131    let p_body = body;
1132
1133    let uri_str = format!("{}/v1/books/scan", configuration.base_path);
1134    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
1135
1136    if let Some(ref user_agent) = configuration.user_agent {
1137        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
1138    }
1139    if let Some(ref token) = configuration.bearer_access_token {
1140        req_builder = req_builder.bearer_auth(token.to_owned());
1141    };
1142    if let Some(body) = p_body {
1143        req_builder = req_builder.body(body);
1144    }
1145
1146    let req = req_builder.build()?;
1147    let resp = configuration.client.execute(req).await?;
1148
1149    let status = resp.status();
1150    let content_type = resp
1151        .headers()
1152        .get("content-type")
1153        .and_then(|v| v.to_str().ok())
1154        .unwrap_or("application/octet-stream");
1155    let content_type = super::ContentType::from(content_type);
1156
1157    if !status.is_client_error() && !status.is_server_error() {
1158        let content = resp.text().await?;
1159        match content_type {
1160            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
1161            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ScanDraft`"))),
1162            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::ScanDraft`")))),
1163        }
1164    } else {
1165        let content = resp.text().await?;
1166        let entity: Option<PostBooksScanError> = serde_json::from_str(&content).ok();
1167        Err(Error::ResponseError(ResponseContent { status, content, entity }))
1168    }
1169}
1170
1171/// Posts a reviewed scanned bill to the ledger. It is the scanner's ONLY write: the voucher goes through the same post() choke point every other source uses, so it is checked to balance (Σdebit == Σcredit) and is idempotent by (scan, scanId) — re-booking the same scan answers posted=false and writes nothing. A bill whose economic identity (vendor, total, issue date) already posted under a DIFFERENT scan is refused 409 unless override is set, which is what stops the same receipt re-scanned into a new file hash from double-booking. An unbalanced voucher is refused 400.
1172pub async fn post_books_scan_book(configuration: &configuration::Configuration, book_request: models::BookRequest) -> Result<models::BookResponse, Error<PostBooksScanBookError>> {
1173    // add a prefix to parameters to efficiently prevent name collisions
1174    let p_book_request = book_request;
1175
1176    let uri_str = format!("{}/v1/books/scan/book", configuration.base_path);
1177    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
1178
1179    if let Some(ref user_agent) = configuration.user_agent {
1180        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
1181    }
1182    if let Some(ref token) = configuration.bearer_access_token {
1183        req_builder = req_builder.bearer_auth(token.to_owned());
1184    };
1185    req_builder = req_builder.json(&p_book_request);
1186
1187    let req = req_builder.build()?;
1188    let resp = configuration.client.execute(req).await?;
1189
1190    let status = resp.status();
1191    let content_type = resp
1192        .headers()
1193        .get("content-type")
1194        .and_then(|v| v.to_str().ok())
1195        .unwrap_or("application/octet-stream");
1196    let content_type = super::ContentType::from(content_type);
1197
1198    if !status.is_client_error() && !status.is_server_error() {
1199        let content = resp.text().await?;
1200        match content_type {
1201            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
1202            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::BookResponse`"))),
1203            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::BookResponse`")))),
1204        }
1205    } else {
1206        let content = resp.text().await?;
1207        let entity: Option<PostBooksScanBookError> = serde_json::from_str(&content).ok();
1208        Err(Error::ResponseError(ResponseContent { status, content, entity }))
1209    }
1210}
1211
1212/// Sync ingests the caller's OWN org from commerce into BOTH ledgers (live and sandbox) and reports how many new vouchers posted to each. It is idempotent — money that has already been booked posts nothing on a repeat — and it is read-only against commerce: it never mints a deposit, a credit or a payout, only the accounting twin of money that already moved.
1213pub async fn post_books_sync(configuration: &configuration::Configuration, ) -> Result<models::SyncTally, Error<PostBooksSyncError>> {
1214
1215    let uri_str = format!("{}/v1/books/sync", configuration.base_path);
1216    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
1217
1218    if let Some(ref user_agent) = configuration.user_agent {
1219        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
1220    }
1221    if let Some(ref token) = configuration.bearer_access_token {
1222        req_builder = req_builder.bearer_auth(token.to_owned());
1223    };
1224
1225    let req = req_builder.build()?;
1226    let resp = configuration.client.execute(req).await?;
1227
1228    let status = resp.status();
1229    let content_type = resp
1230        .headers()
1231        .get("content-type")
1232        .and_then(|v| v.to_str().ok())
1233        .unwrap_or("application/octet-stream");
1234    let content_type = super::ContentType::from(content_type);
1235
1236    if !status.is_client_error() && !status.is_server_error() {
1237        let content = resp.text().await?;
1238        match content_type {
1239            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
1240            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::SyncTally`"))),
1241            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::SyncTally`")))),
1242        }
1243    } else {
1244        let content = resp.text().await?;
1245        let entity: Option<PostBooksSyncError> = serde_json::from_str(&content).ok();
1246        Err(Error::ResponseError(ResponseContent { status, content, entity }))
1247    }
1248}
1249
1250/// Creates or updates one vendor in the org's vendor book, keyed by its canonical name — writing a canonical name that already exists REPLACES that row's aliases and default category. A category given as a slug (\"software\") is normalized to its real COA expense account, and anything unrecognized becomes 5900 Uncategorized rather than a guessed real account. It answers the row exactly as stored, so the caller sees the normalization. Recording a vendor is what makes future bills from it self-classify instead of asking again.
1251pub async fn post_books_vendors(configuration: &configuration::Configuration, vendor_row: models::VendorRow) -> Result<models::VendorRow, Error<PostBooksVendorsError>> {
1252    // add a prefix to parameters to efficiently prevent name collisions
1253    let p_vendor_row = vendor_row;
1254
1255    let uri_str = format!("{}/v1/books/vendors", configuration.base_path);
1256    let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
1257
1258    if let Some(ref user_agent) = configuration.user_agent {
1259        req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
1260    }
1261    if let Some(ref token) = configuration.bearer_access_token {
1262        req_builder = req_builder.bearer_auth(token.to_owned());
1263    };
1264    req_builder = req_builder.json(&p_vendor_row);
1265
1266    let req = req_builder.build()?;
1267    let resp = configuration.client.execute(req).await?;
1268
1269    let status = resp.status();
1270    let content_type = resp
1271        .headers()
1272        .get("content-type")
1273        .and_then(|v| v.to_str().ok())
1274        .unwrap_or("application/octet-stream");
1275    let content_type = super::ContentType::from(content_type);
1276
1277    if !status.is_client_error() && !status.is_server_error() {
1278        let content = resp.text().await?;
1279        match content_type {
1280            ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
1281            ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::VendorRow`"))),
1282            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::VendorRow`")))),
1283        }
1284    } else {
1285        let content = resp.text().await?;
1286        let entity: Option<PostBooksVendorsError> = serde_json::from_str(&content).ok();
1287        Err(Error::ResponseError(ResponseContent { status, content, entity }))
1288    }
1289}
1290