> ## Documentation Index
> Fetch the complete documentation index at: https://docs.smartbills.io/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript SDK

> Official Smartbills SDK for JavaScript and TypeScript applications

## Installation

```bash theme={null}
npm install @smartbills/sdk
```

```bash theme={null}
yarn add @smartbills/sdk
```

## Quick Start

```typescript theme={null}
import { SmartbillsClient } from "@smartbills/sdk";

const client = new SmartbillsClient({
  accessToken: "your-access-token",
  businessId: 42,
  locale: "en-CA",
});

const { data, pagination } = await client.expenses.listBusiness({ limit: 25 });
```

## Configuration

### Client Options

```typescript theme={null}
interface SmartbillsClientOptions {
  baseUrl?: string;
  accessToken?: string;
  businessId?: number;
  locale?: string;
  timeout?: number;
  maxRetries?: number;
  retryDelay?: number;
}
```

| Option        | Type     | Default                     | Description                                       |
| ------------- | -------- | --------------------------- | ------------------------------------------------- |
| `baseUrl`     | `string` | `https://api.smartbills.io` | API base URL                                      |
| `accessToken` | `string` | —                           | OAuth2 bearer token                               |
| `businessId`  | `number` | —                           | Default business context for requests             |
| `locale`      | `string` | —                           | Locale for localized responses (`en-CA`, `fr-CA`) |
| `timeout`     | `number` | —                           | Request timeout in milliseconds                   |
| `maxRetries`  | `number` | —                           | Maximum automatic retries on transient failures   |
| `retryDelay`  | `number` | —                           | Delay between retries in milliseconds             |

### Runtime Configuration

```typescript theme={null}
const client = new SmartbillsClient();

client.setAccessToken("new-token");
client.setBusinessId(42);
client.setLocale("fr-CA");

console.log(client.accessToken);
console.log(client.businessId);
console.log(client.locale);
```

## Services

Every service is accessed as a property on `SmartbillsClient`. All methods accept an optional `RequestOptions` parameter to override `businessId`, `locale`, or pass an `AbortSignal`.

```typescript theme={null}
interface RequestOptions {
  businessId?: number;
  locale?: string;
  signal?: AbortSignal;
}
```

### Expenses

```typescript theme={null}
const expenses = client.expenses;

const list = await expenses.listBusiness({ limit: 25, page: 1 });

const myExpenses = await expenses.listMy({ limit: 10 });

const employeeExpenses = await expenses.listEmployee(employeeId, { limit: 10 });

const expense = await expenses.getById(expenseId);

await expenses.update(expenseId, {
  categoryId: 456,
  note: "Office supplies",
});

await expenses.delete(expenseId);

await expenses.updateCategory(expenseId, { categoryId: 789 });

await expenses.updateNote(expenseId, { note: "Updated note" });

await expenses.updateReport(expenseId, { expenseReportId: 10 });

await expenses.updateReview(expenseId, { reviewed: true });
```

#### Upload Expenses

```typescript theme={null}
const formData = new FormData();
formData.append("files", file);

const uploaded = await expenses.uploadBusiness(formData);

const myUploaded = await expenses.uploadMy(formData);

const employeeUploaded = await expenses.uploadEmployee(employeeId, formData);
```

#### Presigned Upload (S3)

```typescript theme={null}
const { url, fields } = await expenses.presignUpload({
  fileName: "receipt.jpg",
  contentType: "image/jpeg",
});

await uploadFileToS3(url, fields, file);

await expenses.confirmUpload({ key: fields.key });
```

#### Bulk Operations

```typescript theme={null}
await expenses.bulkAssignCategory({ expenseIds: [1, 2, 3], categoryId: 10 });

await expenses.bulkAssignPayerType({ expenseIds: [1, 2], payerType: "BUSINESS" });

await expenses.bulkAssignReport({ expenseIds: [1, 2, 3], expenseReportId: 5 });

await expenses.bulkAssignVendor({ expenseIds: [1, 2], vendorId: 20 });

await expenses.bulkSetNote({ expenseIds: [1, 2], note: "Q1 expenses" });

await expenses.bulkDelete({ expenseIds: [1, 2, 3] });
```

#### Export & Download

```typescript theme={null}
const blob = await expenses.export({ expenseIds: [1, 2, 3] });

const { blob: file, filename } = await expenses.exportWithFilename({
  expenseIds: [1, 2, 3],
});

const attachments = await expenses.downloadAttachments({ expenseIds: [1, 2] });

const single = await expenses.downloadSingleAttachment(expenseId);
```

#### Split

```typescript theme={null}
const splits = await expenses.split(expenseId, {
  splits: [
    { amount: 50, categoryId: 1 },
    { amount: 50, categoryId: 2 },
  ],
});
```

### Expense Reports

```typescript theme={null}
const reports = client.expenseReports;

const list = await reports.list({ status: "draft", limit: 25 });

const report = await reports.getById(reportId);

const mine = await reports.getMine({ status: "submitted" });

const summary = await reports.getSummary();

const pending = await reports.getPendingApprovals();
```

#### Lifecycle

```typescript theme={null}
const created = await reports.create({ name: "Q1 Travel" });

await reports.update(reportId, { name: "Q1 Travel - Updated" });

await reports.submit(reportId);

await reports.approve(reportId, { comment: "Looks good!" });

await reports.reject(reportId, { reason: "Missing receipts" });

await reports.requestChanges(reportId, { comment: "Please add hotel receipt" });

await reports.recall(reportId);

await reports.reimburse(reportId);

await reports.planReimbursement(reportId, { scheduledDate: "2025-03-15" });
```

#### Timeline & Audit

```typescript theme={null}
const timeline = await reports.getTimeline(reportId);

const auditLog = await reports.getAuditLog(reportId);

await reports.addComment(reportId, { content: "Updated totals" });
```

#### Accounting

```typescript theme={null}
await reports.assignLedgerAccount(reportId, { ledgerAccountId: 100 });

await reports.assignExpenseLedgerAccount(reportId, expenseId, {
  ledgerAccountId: 100,
});
```

### Bills

```typescript theme={null}
const bills = client.bills;

const list = await bills.list({ limit: 25 });

const bill = await bills.getById(billId);

const created = await bills.create({
  vendorId: 10,
  amount: 1500,
  dueDate: "2025-04-01",
});

await bills.update(billId, { amount: 1600 });

await bills.delete(billId);
```

#### Bill Status Workflow

```typescript theme={null}
const statusSummary = await bills.getStatusSummary();

const allowedTransitions = await bills.getAllowedTransitions(billId);

await bills.submitForApproval(billId);

await bills.approve(billId, { comment: "Approved" });

await bills.schedulePayment(billId, { paymentDate: "2025-04-01" });

await bills.markPaid(billId);

await bills.cancel(billId, { reason: "Duplicate" });

await bills.revertToDraft(billId);

await bills.reschedulePayment(billId, { paymentDate: "2025-04-15" });
```

#### Bulk Bill Operations

```typescript theme={null}
await bills.bulkApprove({ billIds: [1, 2, 3] });

await bills.bulkMarkPaid({ billIds: [1, 2] });

await bills.bulkSchedulePayment({ billIds: [1, 2], paymentDate: "2025-04-01" });

await bills.bulkDelete({ billIds: [1, 2, 3] });
```

### Vendors

```typescript theme={null}
const vendors = client.vendors;

const list = await vendors.list({ limit: 25 });

const vendor = await vendors.getById(vendorId);

const created = await vendors.create({
  name: "Office Depot",
  email: "billing@officedepot.com",
});

await vendors.update(vendorId, { name: "Office Depot Inc." });

await vendors.delete(vendorId);
```

#### Vendor Business Operations

```typescript theme={null}
const businessVendors = await vendors.listBusiness();

const businessVendor = await vendors.getBusinessVendor(vendorId);

await vendors.createBusiness({ name: "New Supplier" });

await vendors.updateBusiness(vendorId, { name: "Updated Supplier" });

await vendors.deleteBusiness(vendorId);

await vendors.refreshBusiness(vendorId);
```

#### Batch & Import

```typescript theme={null}
await vendors.batchCreate([
  { name: "Vendor A" },
  { name: "Vendor B" },
]);

await vendors.bulkDelete({ vendorIds: [1, 2, 3] });

await vendors.merge({ sourceVendorId: 2, targetVendorId: 1 });

const result = await vendors.importCsv(csvFile);

const template = await vendors.downloadImportTemplate();
```

### Businesses

```typescript theme={null}
const businesses = client.businesses;

const list = await businesses.list({ limit: 10 });

const business = await businesses.getById(businessId);

const count = await businesses.count();

const created = await businesses.create({
  name: "Acme Corp",
  currency: "CAD",
});

await businesses.update(businessId, { name: "Acme Corporation" });

await businesses.delete(businessId);

await businesses.createBrand(businessId, { name: "Acme Brand" });
```

### All Available Services

| Property                | Service                     | Description                             |
| ----------------------- | --------------------------- | --------------------------------------- |
| `users`                 | UserService                 | User profile and account management     |
| `businesses`            | BusinessService             | Business CRUD and listing               |
| `businessUsers`         | BusinessUserService         | Business user management                |
| `invitations`           | InvitationService           | Team invitations                        |
| `memberships`           | MembershipService           | Business memberships                    |
| `receipts`              | ReceiptService              | Receipt CRUD and OCR processing         |
| `bills`                 | BillService                 | Bill management and approval workflow   |
| `transactions`          | TransactionService          | Transaction history                     |
| `expenses`              | ExpenseService              | Expense management with bulk operations |
| `expenseReports`        | ExpenseReportService        | Expense report lifecycle                |
| `expenseReportExpenses` | ExpenseReportExpenseService | Report-expense associations             |
| `expenseReportPayments` | ExpenseReportPaymentService | Report payment tracking                 |
| `expenseJobs`           | ExpenseJobService           | Background processing jobs              |
| `approbations`          | ApprobationService          | Approval management                     |
| `vendors`               | VendorService               | Vendor management with import/merge     |
| `vendorConnections`     | VendorConnectionService     | Vendor connection management            |
| `notifications`         | NotificationService         | In-app notifications                    |
| `paymentMethods`        | PaymentMethodService        | Payment method management               |
| `integrations`          | IntegrationService          | Third-party integrations                |
| `appInstallations`      | AppInstallationService      | App installation management             |
| `emailAccounts`         | EmailAccountService         | Email account configuration             |
| `reports`               | ReportingService            | Reporting and analytics                 |
| `checkout`              | CheckoutService             | Checkout flows                          |
| `connect`               | ConnectService              | Connect account management              |
| `locations`             | LocationService             | Location management                     |
| `tables`                | TableService                | Table management                        |
| `billing`               | BillingService              | Subscription and billing                |
| `workflows`             | WorkflowService             | Automation workflows                    |
| `customers`             | CustomerService             | Customer management                     |
| `departments`           | DepartmentService           | Department management                   |
| `products`              | ProductService              | Product catalog                         |
| `taxes`                 | TaxService                  | Tax configuration                       |
| `promoCodes`            | PromoCodeService            | Promo code management                   |
| `categories`            | CategoryService             | Expense category management             |
| `employees`             | EmployeeService             | Employee management                     |
| `attachments`           | AttachmentService           | File attachment management              |
| `emailForwarding`       | EmailForwardingService      | Email forwarding rules                  |
| `authorizedSenders`     | AuthorizedSenderService     | Authorized sender management            |
| `loyalty`               | LoyaltyService              | Loyalty program management              |

## Error Handling

```typescript theme={null}
import {
  SmartbillsError,
  SmartbillsApiError,
  SmartbillsValidationError,
  SmartbillsAuthenticationError,
  SmartbillsPermissionError,
  SmartbillsNotFoundError,
  SmartbillsRateLimitError,
  SmartbillsNetworkError,
  SmartbillsConflictError,
  SmartbillsQuotaError,
  isSmartbillsError,
  isRetryableError,
} from "@smartbills/sdk";

try {
  const expense = await client.expenses.getById(expenseId);
} catch (error) {
  if (isSmartbillsError(error)) {
    if (error instanceof SmartbillsAuthenticationError) {
      console.error("Authentication failed:", error.message);
    } else if (error instanceof SmartbillsValidationError) {
      error.errors.forEach((fieldError) => {
        console.error(`${fieldError.field}: ${fieldError.message}`);
      });
    } else if (error instanceof SmartbillsNotFoundError) {
      console.error("Resource not found:", error.message);
    } else if (error instanceof SmartbillsRateLimitError) {
      console.error("Rate limited - retry later");
    } else if (error instanceof SmartbillsPermissionError) {
      console.error("Insufficient permissions:", error.message);
    } else if (error instanceof SmartbillsNetworkError) {
      console.error("Network error:", error.message);
    }
  }
}
```

### Error Types

| Error Class                     | HTTP Status | Description                   |
| ------------------------------- | ----------- | ----------------------------- |
| `SmartbillsError`               | —           | Base error class              |
| `SmartbillsApiError`            | Various     | Generic API error             |
| `SmartbillsValidationError`     | 400         | Field-level validation errors |
| `SmartbillsAuthenticationError` | 401         | Invalid or expired token      |
| `SmartbillsPermissionError`     | 403         | Insufficient permissions      |
| `SmartbillsNotFoundError`       | 404         | Resource not found            |
| `SmartbillsConflictError`       | 409         | Resource conflict             |
| `SmartbillsRateLimitError`      | 429         | Rate limit exceeded           |
| `SmartbillsQuotaError`          | —           | Quota exceeded                |
| `SmartbillsNetworkError`        | —           | Network connectivity issues   |

### Type Guards

```typescript theme={null}
import {
  isSmartbillsError,
  isApiError,
  isValidationError,
  isRateLimitError,
  isNetworkError,
  isAuthenticationError,
  isPermissionError,
  isNotFoundError,
  isConflictError,
  isQuotaError,
  isRetryableError,
} from "@smartbills/sdk";
```

## Pagination

All list endpoints return a standard paginated response:

```typescript theme={null}
interface SBListResponse<T> {
  data: T[];
  pagination: SBPagination;
}

interface SBPagination {
  count: number;
  limit: number;
  currentPage: number;
  pageCount: number;
}
```

```typescript theme={null}
const page1 = await client.expenses.listBusiness({ page: 1, limit: 25 });

console.log(page1.pagination.count);
console.log(page1.pagination.pageCount);

if (page1.pagination.currentPage < page1.pagination.pageCount) {
  const page2 = await client.expenses.listBusiness({ page: 2, limit: 25 });
}
```

### Sorting

```typescript theme={null}
const sorted = await client.expenses.listBusiness({
  sortBy: "createdAt",
  sortDirection: "desc",
  limit: 25,
});
```

## Common Types

```typescript theme={null}
import type {
  SBEntity,
  SBTimestamps,
  SBListResponse,
  SBPagination,
  PaginationRequest,
  SBAddress,
  SBCoordinate,
  SBMoney,
  SBBillingAddress,
  SBBatchResponse,
  SBBulkActionResponse,
  SBFileDownloadResponse,
  RequestOptions,
} from "@smartbills/sdk";
```

## File Uploads

### S3 Presigned Upload (Recommended)

```typescript theme={null}
import { uploadFileToS3 } from "@smartbills/sdk";

const presigned = await client.expenses.presignUpload({
  fileName: "receipt.jpg",
  contentType: "image/jpeg",
});

await uploadFileToS3(presigned.url, presigned.fields, file);

await client.expenses.confirmUpload({ key: presigned.fields.key });
```

### Direct FormData Upload

```typescript theme={null}
const formData = new FormData();
formData.append("files", fileInput.files[0]);

const results = await client.expenses.uploadBusiness(formData);
```

## TypeScript Support

The SDK is written in TypeScript and ships with full type definitions. All entity types, request types, and response types are exported:

```typescript theme={null}
import type {
  SBTransaction,
  SBExpenseReport,
  SBBusiness,
  SBVendor,
  SBBill,
  ExpenseReportStatus,
  BillStatus,
  PayerType,
  MembershipRole,
} from "@smartbills/sdk";
```

## Examples

### Express.js Integration

```typescript theme={null}
import express from "express";
import { SmartbillsClient } from "@smartbills/sdk";

const app = express();
const client = new SmartbillsClient({
  accessToken: process.env.SMARTBILLS_ACCESS_TOKEN,
  businessId: parseInt(process.env.SMARTBILLS_BUSINESS_ID!),
});

app.get("/expenses", async (req, res) => {
  const expenses = await client.expenses.listBusiness({
    page: parseInt(req.query.page as string) || 1,
    limit: 25,
  });
  res.json(expenses);
});

app.listen(3000);
```

### Next.js Server Action

```typescript theme={null}
"use server";

import { SmartbillsClient } from "@smartbills/sdk";

const client = new SmartbillsClient({
  accessToken: process.env.SMARTBILLS_ACCESS_TOKEN,
  businessId: parseInt(process.env.SMARTBILLS_BUSINESS_ID!),
});

export async function getExpenses(page: number = 1) {
  return client.expenses.listBusiness({ page, limit: 25 });
}

export async function createExpenseReport(name: string, expenseIds: number[]) {
  return client.expenseReports.create({ name });
}
```

<Tip>
  **Use environment variables** — Never hardcode access tokens in your source code.
</Tip>

<Tip>
  **Handle errors gracefully** — Always wrap API calls in try-catch blocks and use the type guards to handle specific error types.
</Tip>

<Tip>
  **Use TypeScript** — Take advantage of full type safety for better developer experience and catch issues at compile time.
</Tip>
