Official Python SDK for Essabu - Unified SaaS billing, accounting, HR, and business platform
Project description
Essabu Python SDK
The official Python SDK for the Essabu platform -- a unified SaaS solution for billing, accounting, HR, CRM, payments, project management, asset tracking, and electronic invoicing. This SDK gives you a single, consistent Pythonic interface to all eight platform modules, complete with pagination helpers, a rich exception hierarchy, automatic retries, and environment-variable-based configuration.
Table of Contents
- Installation
- Quick Start
- Configuration
- Modules
- Pagination
- Error Handling
- Async Support
- Webhooks
- Examples
- Contributing
- License
Installation
Install the SDK using pip, poetry, or pipenv. The package is published on PyPI as essabu. It requires Python 3.10 or higher and installs httpx and pydantic as dependencies automatically.
# pip
pip install essabu
# poetry
poetry add essabu
# pipenv
pipenv install essabu
Requirements: Python 3.10+ | Dependencies: httpx, pydantic
Quick Start
Initialize the client with your API key and tenant ID, then interact with any of the eight modules using dot notation. Each module exposes resource objects with standard CRUD methods (create, list, retrieve, update, delete). The example below demonstrates one operation per module to illustrate the unified interface. Always call client.close() when done, or use a context manager.
from essabu import Essabu
client = Essabu(api_key="esa_live_xxx", tenant_id="your-tenant-id")
# HR -- Create an employee
employee = client.hr.employees.create(
first_name="Jean",
last_name="Mukendi",
email="jean.mukendi@example.com",
department_id="dept-001",
)
print(employee)
# Accounting -- Create an invoice
invoice = client.accounting.invoices.create(
customer_id="cust-001",
items=[{"description": "Consulting", "quantity": 10, "unit_price": "150.00"}],
currency="USD",
)
# Trade / CRM -- List customers with pagination
customers = client.trade.customers.list(page=1, page_size=25)
for c in customers.data:
print(c["name"])
# Payments -- Create a payment intent
intent = client.payment.payment_intents.create(
amount="500.00",
currency="USD",
payment_method="mobile_money",
)
# E-Invoice -- Submit to tax authority
einvoice = client.einvoice.invoices.create(
invoice_id="inv-uuid",
customer_tin="123456789",
customer_name="ACME Corp",
items=[{"description": "Service", "quantity": 1, "unit_price": 500, "tax_rate": 16}],
currency="CDF",
)
# Project -- Create a project
project = client.project.projects.create(
name="Website Redesign",
start_date="2026-04-01",
end_date="2026-09-30",
budget=50000,
currency="USD",
)
# Asset -- Register a fixed asset
asset = client.asset.assets.create(
name="Dell PowerEdge R750",
category="IT Equipment",
purchase_price=8500,
currency="USD",
depreciation_method="straight_line",
)
# Identity -- Manage users and roles
users = client.identity.users.list(page_size=20)
# Always close when done (or use a context manager)
client.close()
Context Manager
Use the context manager pattern to ensure the underlying HTTP client is automatically closed when the block exits, even if an exception is raised. This is the recommended approach for scripts, CLI tools, and any code where the client has a bounded lifetime.
with Essabu(api_key="esa_live_xxx", tenant_id="your-tenant-id") as client:
employees = client.hr.employees.list()
# client is automatically closed when the block exits
Configuration
Explicit Parameters
Create the client by passing configuration directly as constructor arguments. The api_key and tenant_id are required. The base_url defaults to https://api.essabu.com. The timeout controls the HTTP request timeout in seconds (default 30), and max_retries sets the number of automatic retry attempts for transient errors (default 3).
from essabu import Essabu
client = Essabu(
api_key="esa_live_xxx",
tenant_id="your-tenant-id",
base_url="https://api.essabu.com",
timeout=30.0,
max_retries=3,
)
Environment Variables
Set environment variables to configure the client without hardcoding secrets in source code. This is the recommended approach for production deployments, CI/CD pipelines, and containerized environments. All variables are optional when the corresponding constructor argument is provided.
export ESSABU_API_KEY=esa_live_your_api_key
export ESSABU_TENANT_ID=your-tenant-id
export ESSABU_BASE_URL=https://api.essabu.com # optional
export ESSABU_TIMEOUT=30.0 # optional
export ESSABU_MAX_RETRIES=3 # optional
When all required environment variables are set, the client can be instantiated with no arguments. Missing required values (ESSABU_API_KEY, ESSABU_TENANT_ID) raise a ConfigurationError at instantiation time.
client = Essabu() # reads all values from environment
Configuration Reference
| Parameter | Env Variable | Default | Description |
|---|---|---|---|
api_key |
ESSABU_API_KEY |
-- | API key (required) |
tenant_id |
ESSABU_TENANT_ID |
-- | Tenant ID (required) |
base_url |
ESSABU_BASE_URL |
https://api.essabu.com |
API base URL |
timeout |
ESSABU_TIMEOUT |
30.0 |
Request timeout (seconds) |
max_retries |
ESSABU_MAX_RETRIES |
3 |
Max retry attempts |
Modules
All modules are accessible via dot notation on the client. They are lazy-loaded -- only initialized when first accessed.
HR
Employees, departments, contracts, payroll, attendance, leaves, benefits, loans, expenses, performance reviews, recruitment, training, onboarding, shifts, timesheets, and more.
Manage the full employee lifecycle including hiring, department assignments, payroll processing, and leave tracking. The list method supports filtering by department and page_size. The payroll.create method processes payroll for a specific period and department. The reports.headcount method generates organizational analytics by year.
# Employee management
employees = client.hr.employees.list(page_size=50, department="engineering")
employee = client.hr.employees.create(first_name="Jean", last_name="Dupont", email="jean@co.com")
client.hr.employees.update("emp-id", salary=55000)
# Payroll
run = client.hr.payroll.create(period="2026-03", department_id="dept-uuid")
# Leave management
leave = client.hr.leaves.create(employee_id="emp-id", type="annual", start_date="2026-04-01", end_date="2026-04-05")
# Reports
headcount = client.hr.reports.headcount(year=2026)
Full reference: HR Module Wiki
Accounting
Chart of accounts, journal entries, invoices, payments, credit notes, quotes, fiscal years, currencies, exchange rates, tax rates, wallets, inventory, insurance, price lists, and financial reports.
Create and manage the chart of accounts with unique account codes and types (asset, liability, equity, revenue, expense). Record double-entry journal entries with balanced debit/credit lines. Generate standard financial reports including balance sheet, income statement, trial balance, and cash flow statements for any date range.
# Chart of accounts
account = client.accounting.accounts.create(code="4010", name="Sales", type="revenue")
# Journal entries
entry = client.accounting.journal_entries.create(
journal_id="jnl-uuid", date="2026-03-26", reference="JV-001",
lines=[{"account_id": "acc-1", "debit": 1000, "credit": 0}, {"account_id": "acc-2", "debit": 0, "credit": 1000}],
)
# Financial reports
bs = client.accounting.reports.balance_sheet(date="2026-03-31")
pnl = client.accounting.reports.income_statement(start_date="2026-01-01", end_date="2026-03-31")
Full reference: Accounting Module Wiki
Identity
Authentication, users, roles, permissions, tenants, companies, branches, profiles, sessions, and API key management.
Authenticate users with email/password to obtain access and refresh tokens. Manage user accounts, roles with granular permissions, and multi-tenant organizations with branches. The auth.login method returns JWT tokens, and auth.refresh obtains new tokens without re-entering credentials. Throws AuthenticationError for invalid credentials.
# Login
tokens = client.identity.auth.login(email="admin@co.com", password="secret")
# User management
user = client.identity.users.create(email="new@co.com", first_name="Alice", last_name="Martin")
# Roles
role = client.identity.roles.create(name="Accountant", permissions=["accounting.read", "accounting.write"])
Full reference: Identity Module Wiki
Trade / CRM
Customers, contacts, opportunities, sales orders, purchase orders, products, suppliers, deliveries, contracts, campaigns, activities, warehouses, stock, and receipts.
Manage the complete CRM and sales pipeline from customer creation through opportunity tracking to order fulfillment. The customers.create method requires name and email. The opportunities.create method tracks deals with value, currency, and stage for pipeline management. The sales_orders.create method creates orders with line items for fulfillment tracking.
# Customers
customer = client.trade.customers.create(name="ACME Corp", email="contact@acme.com")
# Sales pipeline
deal = client.trade.opportunities.create(
title="Enterprise Deal", customer_id="cust-uuid", value=50000, stage="qualification",
)
# Orders
order = client.trade.sales_orders.create(
customer_id="cust-uuid",
lines=[{"product_id": "prod-uuid", "quantity": 10, "unit_price": 25}],
)
Full reference: Trade Module Wiki
Payment
Payment intents, transactions, refunds, subscriptions, loan applications and products, KYC, financial accounts, and payment reports.
Process payments through the intent lifecycle: create, confirm, and capture. The payment_intents.create method requires amount, currency, and payment_method. Subscription plans define recurring billing with interval (month/year). Loan applications go through approval and disbursement stages. KYC profiles and documents handle compliance verification.
# Payment intent lifecycle
intent = client.payment.payment_intents.create(amount=5000, currency="USD", payment_method="mobile_money")
client.payment.payment_intents.confirm(intent["id"])
client.payment.payment_intents.capture(intent["id"], amount=5000)
# Subscriptions
plan = client.payment.subscription_plans.create(name="Pro", amount=29.99, interval="month")
sub = client.payment.subscriptions.create(customer_id="cust-uuid", plan_id=plan["id"])
# Loans
app = client.payment.loan_applications.create(customer_id="cust-uuid", amount=5000, purpose="Working capital")
Full reference: Payment Module Wiki
E-Invoice
Electronic invoices, tax authority submissions, verification, compliance records, and statistics.
Create e-invoices with tax identification numbers and item-level tax rates, submit them to the configured tax authority (e.g., OBR), and verify their authenticity via QR code or reference number. The submission workflow goes through create, submit, and check_status stages. The verification.verify method validates invoices against the tax authority registry.
# Create and submit
einvoice = client.einvoice.invoices.create(invoice_id="inv-uuid", customer_tin="123456789", ...)
submission = client.einvoice.submissions.create(invoice_id=einvoice["id"], authority="OBR")
result = client.einvoice.submissions.submit(submission["id"])
status = client.einvoice.submissions.check_status(submission["id"])
# Verify
verification = client.einvoice.verification.verify(qr_code="https://tax.gov/verify/ABC123")
Full reference: E-Invoice Module Wiki
Project
Projects, tasks, milestones, resource allocations, task comments, and project reports.
Create and manage projects with budgets, timelines, and team assignments. Tasks support priority levels, due dates, estimated hours, and progress tracking. Milestones mark key deliverables, and resource allocations enable capacity planning across team members. All resources are scoped to a project_id for filtering.
project = client.project.projects.create(name="Website Redesign", start_date="2026-04-01", end_date="2026-09-30")
task = client.project.tasks.create(project_id=project["id"], title="Design mockups", priority="high")
milestone = client.project.milestones.create(project_id=project["id"], name="Phase 1 Complete", due_date="2026-05-01")
Full reference: Project Module Wiki
Asset
Fixed assets, vehicles, depreciation, maintenance schedules and logs, fuel logs, and trip logs.
Register fixed assets with purchase details and depreciation methods, manage a vehicle fleet with driver assignments, and track maintenance, fuel consumption, and trips. The assets.create method supports "straight_line" and "declining_balance" depreciation. Vehicle-related resources (fuel logs, trip logs, maintenance) are linked via vehicle_id.
asset = client.asset.assets.create(name="Server", category="IT", purchase_price=8500, depreciation_method="straight_line")
vehicle = client.asset.vehicles.create(make="Toyota", model="Hilux", year=2025, license_plate="KIN-1234-AB")
fuel = client.asset.fuel_logs.create(vehicle_id=vehicle["id"], liters=60.5, cost_per_liter=1.85)
trip = client.asset.trip_logs.create(vehicle_id=vehicle["id"], origin="Kinshasa", destination="Matadi")
Full reference: Asset Module Wiki
Pagination
All list() methods return a PageResponse object with pagination metadata.
Manual Pagination
Fetch a single page of results with explicit page and page_size parameters. The returned PageResponse includes total (total items across all pages), page (current page number), total_pages, and navigation booleans has_next and has_previous. Access the items via the data list property. Check has_next to determine whether more pages are available.
page = client.hr.employees.list(page=1, page_size=50)
print(f"Page {page.page}/{page.total_pages} | Total: {page.total}")
for employee in page.data:
print(employee["first_name"])
# Check for next page
if page.has_next:
next_page = client.hr.employees.list(page=page.page + 1, page_size=50)
Automatic Iteration
Use list_all() to get a generator that automatically fetches every page in sequence. Each iteration yields a PageResponse for one page. This is ideal for processing large datasets without manually managing page numbers. The page_size parameter controls the number of items fetched per HTTP request.
for page in client.hr.employees.list_all(page_size=100):
for employee in page.data:
print(employee["email"])
Collecting All Results
Accumulate all items from every page into a single list. This pattern uses list_all() with extend() to build a complete in-memory collection. Use this when you need random access to the full dataset. Be mindful of memory usage for very large collections; prefer streaming with list_all() when possible.
all_employees = []
for page in client.hr.employees.list_all(page_size=100):
all_employees.extend(page.data)
PageResponse Properties
| Property | Type | Description |
|---|---|---|
data |
list[dict] |
Items on the current page |
total |
int |
Total number of items |
page |
int |
Current page number |
page_size |
int |
Items per page |
total_pages |
int |
Total number of pages |
has_next |
bool |
Whether a next page exists |
has_previous |
bool |
Whether a previous page exists |
Error Handling
The SDK provides a rich exception hierarchy. All exceptions inherit from EssabuError.
Import specific exception types to handle different error scenarios. Each exception exposes status_code and message properties. ValidationError additionally provides an errors list with per-field details. RateLimitError includes a retry_after property indicating how many seconds to wait before retrying. Catch EssabuError as a fallback for any unexpected API errors.
from essabu.common.exceptions import (
EssabuError,
AuthenticationError,
AuthorizationError,
NotFoundError,
ConflictError,
ValidationError,
RateLimitError,
ServerError,
)
try:
employee = client.hr.employees.retrieve("nonexistent-id")
except NotFoundError as e:
print(f"Not found: {e.message}")
except ValidationError as e:
for err in e.errors:
print(f"Field error: {err}")
except AuthenticationError:
print("Invalid API key")
except AuthorizationError:
print("Insufficient permissions")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after}s")
except ServerError as e:
print(f"Server error ({e.status_code})")
except EssabuError as e:
print(f"API error: {e.status_code} - {e.message}")
Exception Hierarchy
| Exception | HTTP Status | Description |
|---|---|---|
EssabuError |
any | Base exception |
AuthenticationError |
401 | Invalid API key / token |
AuthorizationError |
403 | Insufficient permissions |
NotFoundError |
404 | Resource not found |
ConflictError |
409 | Resource conflict |
ValidationError |
422 | Request validation failed |
RateLimitError |
429 | Rate limit exceeded |
ServerError |
5xx | Server-side error |
Async Support
The SDK is built on httpx, which provides both sync and async HTTP clients. The default client uses synchronous operations. For async usage, the underlying httpx.AsyncClient can be leveraged:
Initialize the client as usual and call SDK methods within an async function. The client can be used in both sync and async contexts. Remember to call client.close() when done to release the underlying HTTP connection pool. For long-running async applications, consider using the context manager pattern within an async context.
import asyncio
from essabu import Essabu
async def main():
client = Essabu(api_key="esa_live_xxx", tenant_id="tenant-id")
employees = client.hr.employees.list(page_size=10)
print(f"Found {employees.total} employees")
client.close()
asyncio.run(main())
Webhooks
Essabu sends webhook events signed with HMAC-SHA256. Verify the X-Essabu-Signature header before processing.
Set up a webhook endpoint that verifies the HMAC-SHA256 signature from the X-Essabu-Signature header against your webhook secret. The payload is a JSON object with type (event name like "invoice.created" or "payment.succeeded") and data (the event payload). Always return a 200 response promptly; process events asynchronously if needed. The example below uses Flask, but the verification logic works with any web framework.
import hashlib
import hmac
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = "whsec_your_webhook_secret"
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
@app.route("/webhooks/essabu", methods=["POST"])
def handle_webhook():
payload = request.get_data()
signature = request.headers.get("X-Essabu-Signature", "")
if not verify_signature(payload, signature, WEBHOOK_SECRET):
return jsonify({"error": "Invalid signature"}), 401
event = json.loads(payload)
if event["type"] == "invoice.created":
print(f"New invoice: {event['data']['id']}")
elif event["type"] == "payment.succeeded":
print(f"Payment: {event['data']['amount']}")
return jsonify({"received": True}), 200
Full reference: Webhooks Wiki
Examples
Complete examples are in the examples/ directory.
| Example | Description | Module |
|---|---|---|
authentication.py |
Login, token refresh, password reset | Identity |
create_employee.py |
Employee creation and management | HR |
create_invoice.py |
Invoice creation and payment | Accounting |
crm_pipeline.py |
CRM pipeline workflow | Trade |
einvoice_submit.py |
E-invoice submission | E-Invoice |
loan_application.py |
Loan application workflow | Payment |
process_payment.py |
Payment intent lifecycle | Payment |
webhook_handler.py |
Webhook handler with Flask | Webhooks |
Set the required environment variables and run any example directly with Python. Each example is self-contained and demonstrates a complete workflow for its module. Use test API keys (esa_test_) for development to avoid affecting production data.
# Run an example
export ESSABU_API_KEY=esa_test_xxx
export ESSABU_TENANT_ID=your-tenant-id
python examples/create_employee.py
Contributing
Contributions are welcome. Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feat/amazing-feature) - Write tests for your changes
- Ensure all tests pass (
pytest) - Ensure type checks pass (
mypy essabu/) - Ensure linting passes (
ruff check essabu/) - Commit your changes using Conventional Commits
- Open a Pull Request
Development Setup
Clone the repository and install with development dependencies. The [dev] extra includes pytest, mypy, ruff, and black for testing, type checking, linting, and formatting respectively. Run all checks before submitting a pull request to ensure your changes pass CI.
git clone https://github.com/essabu/essabu-python.git
cd essabu-python
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Type checking
mypy essabu/
# Linting
ruff check essabu/
# Formatting
black essabu/
License
This project is licensed under the MIT License -- see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file essabu-0.1.1.tar.gz.
File metadata
- Download URL: essabu-0.1.1.tar.gz
- Upload date:
- Size: 58.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d20116b1d9b7e8c80eca07dc61b5309b5915ce8ad9afae8faef00e97a1e0cd5
|
|
| MD5 |
32f72f3dd834238b01e402093a93058d
|
|
| BLAKE2b-256 |
92f136aae4cbd230bee34510e53f6b322ccac4278767033632cf206f21d16fcd
|
File details
Details for the file essabu-0.1.1-py3-none-any.whl.
File metadata
- Download URL: essabu-0.1.1-py3-none-any.whl
- Upload date:
- Size: 119.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c29f481ca767aafb63bba4ffca5a51e20eb63234129380156e987f7d008ab8d
|
|
| MD5 |
b01515168a3402203d0f01437cfa762e
|
|
| BLAKE2b-256 |
db2ad93c55e47575c4c3634992de6d087c1ce8f5b4028f71ca6731041f59bcae
|