Contents

emotiveapps/zohobooksclient

A simple Zoho Books API client written in Swift.

Features

  • Full async/await support with Swift concurrency
  • Actor-based thread-safe API client
  • Automatic OAuth token refresh
  • Built-in rate limiting (100 requests/minute)
  • Support for all Zoho data center regions (US, EU, IN, AU, JP)
  • Comprehensive model types for Zoho Books entities

Supported Endpoints

  • Contacts - Customers and vendors
  • Invoices - Create, fetch, and mark as sent
  • Expenses - With attachment upload support
  • Payments - Customer payments
  • Chart of Accounts - Account management
  • Items - Products and services
  • Taxes - Tax rates and exemptions

Strict Concurrency

This library is fully compatible with Swift's strict concurrency checking. All types are marked Sendable, and the main classes (ZohoBooksClient, ZohoOAuth) are actor types which are inherently thread-safe. The project compiles cleanly with -strict-concurrency=complete.

Installation

Swift Package Manager

Add the following to your Package.swift:

dependencies: [
    .package(url: "https://github.com/yourusername/ZohoBooksClient.git", from: "0.1.0")
]

Or add it via Xcode: File > Add Package Dependencies...

Usage

Configuration

import ZohoBooksClient

let config = ZohoConfig(
    clientId: "your-client-id",
    clientSecret: "your-client-secret",
    accessToken: "your-access-token",
    refreshToken: "your-refresh-token",
    organizationId: "your-organization-id",
    region: .com  // .com, .eu, .in, .au, or .jp
)

let client = ZohoBooksClient(config: config)

Fetching Contacts

// Fetch all customers
let customers = try await client.fetchContacts(contactType: "customer")

// Search for a specific contact
if let contact = try await client.searchContactByName("Acme Corp") {
    print("Found: \(contact.contactName ?? "")")
}

Creating an Invoice

let lineItems = [
    ZBInvoiceLineItemRequest(
        name: "Consulting",
        description: "Development work",
        rate: 150.0,
        quantity: 10.0
    )
]

let invoice = ZBInvoiceCreateRequest(
    customerId: "customer-id",
    invoiceNumber: "INV-001",
    date: "2024-01-15",
    lineItems: lineItems
)

let created = try await client.createInvoice(invoice)
print("Created invoice: \(created.invoiceId ?? "")")

// Mark as sent
if let invoiceId = created.invoiceId {
    try await client.markInvoiceAsSent(invoiceId)
}

Creating an Expense with Attachment

let expense = ZBExpenseCreateRequest(
    accountId: "expense-account-id",
    date: "2024-01-15",
    amount: 250.50,
    description: "Office supplies"
)

let created = try await client.createExpense(expense)

// Upload receipt
if let expenseId = created.expenseId {
    let receiptData = try Data(contentsOf: receiptURL)
    try await client.uploadExpenseAttachment(
        expenseId: expenseId,
        fileData: receiptData,
        filename: "receipt.pdf"
    )
}

Token Refresh Callback

You can persist refreshed tokens by setting a callback:

let client = ZohoBooksClient(config: config)

await client.oauthManager.onTokenRefresh = { accessToken, refreshToken in
    // Persist the new tokens
    await saveTokens(accessToken: accessToken, refreshToken: refreshToken)
}

Requirements

  • Swift 5.9+
  • macOS 12+ / iOS 15+ / tvOS 15+ / watchOS 8+

Zoho API Documentation

For more information about the Zoho Books API, see:

License

MIT License - See LICENSE file for details.

Package Metadata

Repository: emotiveapps/zohobooksclient

Default branch: main

README: README.md