| Crates.io | quick-oxibooks |
| lib.rs | quick-oxibooks |
| version | 0.2.3 |
| created_at | 2025-11-13 02:12:54.549832+00 |
| updated_at | 2025-11-28 19:28:08.137366+00 |
| description | A Rust client library for QuickBooks Online API |
| homepage | |
| repository | https://github.com/exotik850/quick-oxibooks |
| max_upload_size | |
| id | 1930305 |
| size | 231,513 |
The
quick-oxibooks crate is a small, ergonomic client for the QuickBooks Online API built on top of quickbooks-types. It focuses on a simple, type-safe workflow for CRUD, queries, reports, and batch operations, with rate-limiting and OAuth2 token usage handled by a lightweight context.
QuickBooks API docs: https://developer.intuit.com/app/developer/qbo/docs/get-started
This crate:
ureqQBContext and Environment for auth/config + rate limitingquickbooks-types::reportsquick_oxibooks::types::* (from quickbooks-types)quickbooks-types)This crate does not:
ureq)[dependencies]
quick-oxibooks = "0.1.2"
Optional features:
log cratequickbooks-types[dependencies]
quick-oxibooks = { version = "0.1.2", features = ["pdf", "attachments"] }
quick_oxibooks::client:
QBContext, RefreshableQBContext, Environmentquick_oxibooks::functions:
create::QBCreate, read::QBRead, delete::QBDeletequery::QBQueryreports::QBReportattachment (feature = "attachments"), pdf (feature = "pdf")quick_oxibooks::batch:
QBBatchOperation, BatchIteratorquick_oxibooks::error:
APIError, APIErrorInnerquick_oxibooks::types::*:
quickbooks-types (entities, reports, helpers)Create a context, then perform CRUD and queries with typed entities from quickbooks-types.
use quick_oxibooks::{Environment, QBContext};
use quick_oxibooks::functions::{create::QBCreate, read::QBRead, delete::QBDelete, query::QBQuery};
use quick_oxibooks::error::APIError;
use quick_oxibooks::types::{Customer, Invoice};
use ureq::Agent;
fn main() -> Result<(), APIError> {
// 1) Create a QBO context (provide your existing OAuth2 Bearer token and company ID)
let client = Agent::new_with_defaults();
let qb = QBContext::new(
Environment::SANDBOX,
"your_company_id".to_string(),
"your_access_token".to_string(),
&client,
)?;
// 2) Create
let mut customer = Customer::default();
customer.display_name = Some("Acme Corp".into());
let created = customer.create(&qb, &client)?;
println!("Created customer ID = {:?}", created.id);
// 3) Read (in-place refresh by ID)
let mut c = Customer::default();
c.id = created.id.clone();
c.read(&qb, &client)?;
println!("Refreshed name = {:?}", c.display_name);
// 4) Query (SQL-like)
let invoices: Vec<Invoice> = Invoice::query(
"WHERE TotalAmt > '100.00' ORDER BY MetaData.CreateTime DESC",
Some(10),
&qb,
&client,
)?;
println!("Found {} invoices over $100", invoices.len());
// 5) Delete (requires ID + sync_token)
// let deleted = created.delete(&qb, &client)?;
// println!("Deleted status = {}", deleted.status);
Ok(())
}
Tip: You can also build contexts from env vars: QBContext::new_from_env(Environment::..., &client) expects QB_COMPANY_ID and QB_ACCESS_TOKEN.
Use strongly-typed report kinds and optional typed parameters from quickbooks-types::reports.
use chrono::NaiveDate;
use quick_oxibooks::{Environment, QBContext};
use quick_oxibooks::functions::reports::QBReport;
use quick_oxibooks::types::reports::{Report, types::*, params::*};
use ureq::Agent;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Agent::new_with_defaults();
let qb = QBContext::new(
Environment::SANDBOX,
"company".into(),
"access_token".into(),
&client,
)?;
// Example: Balance Sheet with typed params
let params = BalanceSheetParams::new()
.start_date(NaiveDate::from_ymd_opt(2024, 1, 1).unwrap())
.end_date(NaiveDate::from_ymd_opt(2024, 12, 31).unwrap())
.accounting_method(AccountingMethod::Accrual)
.summarize_column_by(SummarizeColumnBy::Month);
let report: Report = Report::get(&qb, &client, &BalanceSheet, Some(params))?;
println!("Report name = {:?}", report.name());
Ok(())
}
Batch multiple operations (query/create/update/delete) into one request and correlate responses.
use quick_oxibooks::{QBContext, Environment};
use quick_oxibooks::batch::{QBBatchOperation, BatchIterator};
use quick_oxibooks::types::{Invoice, Vendor};
use ureq::Agent;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Agent::new_with_defaults();
let qb = QBContext::new(
Environment::SANDBOX,
"company".into(),
"token".into(),
&client,
)?;
// Prepare a few operations
let ops = vec![
// Query invoices
QBBatchOperation::query("SELECT * FROM Invoice WHERE TotalAmt > '250.00' MAXRESULTS 5"),
// Create a vendor
QBBatchOperation::create(Vendor {
display_name: Some("New Supplier LLC".into()),
..Default::default()
}),
// Update an invoice (provide id + sync_token as needed)
QBBatchOperation::update(Invoice {
id: Some("123".into()),
// .. other fields to change here
..Default::default()
}),
];
// Execute and inspect
let results = ops.batch(&qb, &client)?;
for (op, resp) in results {
println!("{op:?} -> {resp:?}");
}
Ok(())
}
logquickbooks-types Polars helpers (feature passthrough)Enable one or more:
[dependencies]
quick-oxibooks = { version = "0.1.0", features = ["attachments", "pdf", "logging"] }
APIError wraps all errors surfaced by the client, including HTTP, JSON, and QBO faults, with variants in APIErrorInner such as:
use quick_oxibooks::error::{APIError, APIErrorInner};
fn handle(err: APIError) {
match &*err {
APIErrorInner::BadRequest(qb) => {
eprintln!("QBO error: {}", qb);
}
APIErrorInner::ThrottleLimitReached => {
eprintln!("Hit QBO rate limits; wait ~60s before retrying");
}
other => {
eprintln!("Other error: {other}");
}
}
}
RefreshableQBContext can renew it if you have a refresh token.DisplayName, TotalAmt).sync_token are required for deletion and some updates.Issues and PRs are welcome. If you need additional endpoints, features, or ergonomics, open an issue describing your use case.
Basic workflow:
cargo build && cargo testThis project is licensed under the MIT License. See LICENSE for details.
For detailed documentation, visit docs.rs/quick-oxibooks.