# Handling Bills and Expenses for NetSuite, QuickBooks, Xero, and Exact Online

Apideck empowers Vertical SaaS and fintechs to easily build and maintain accounting integrations that are crucial to the success of modern expense management solutions.

Over half of SMEs base their choice of expense solution on the quality of accounting integrations. That’s why embedding seamless accounting connectivity isn’t just a feature. It’s a product differentiator.

Whether you’re a corporate card provider, Vertical SaaS, expense management platform, or neobank, Apideck helps you embed accounting automation in your product. Cutting months off development time while delivering a best-in-class experience.

Apideck's [Accounting API](/apis/accounting/reference) provides a single REST interface to automate the complexities of expense reconciliation, multi-currency accounting, expense categorization and enabling users to correct errors. This guide explains how to handle expense reports for NetSuite, QuickBooks, Xero, and Exact Online using the unified **Bills** and **Expenses** resources.

---

## Why a Unified API?

Unifying accounting integrations through Apideck reduces development effort and simplifies support. Instead of writing separate integrations for each provider, you connect once via the **Apideck Vault** and use standard endpoints and models. Apideck then maps your request to the correct downstream object:

- **QuickBooks**: `Purchase`
- **Xero**: `Bank Transaction`
- **NetSuite**: `Vendor Bill` and `Expense Report`
- **Exact Online**: `Purchase Invoice`

### Benefits of a Unified Approach

- **Less integration logic** – No more provider-specific field names or endpoints.
- **Standardized data model** – Use the same Expense and Bill structures across providers.
- **Faster onboarding** – Minimal changes required when new platforms are added.

Learn more about the concept of [What's a Unified API](https://www.apideck.com/blog/what-is-a-unified-api).

---

## For Credit Card Providers: Xero Bank Feeds Integration

If you're a credit card provider or financial institution looking to automatically send transaction data directly into your customers' Xero accounts, consider using **Xero Bank Feeds** instead of traditional expense APIs. Bank feeds enable real-time transaction streaming, eliminating manual CSV uploads and improving reconciliation workflows.

Learn more in our dedicated guide: [Xero Bank Feeds Integration](/guides/bank-feeds-xero)

---

## When to Use Bills vs. Expenses

| Resource                                                                              | Purpose                                                                                                                                                                                  | When to Use                                                                                                                                     |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **[Bills](/apis/accounting/reference#tag/Bills)** (`POST /accounting/bills`)          | Represents a vendor invoice awaiting payment. Required fields include `id`, `bill_date`, `due_date`, `line_items`, and `total`.                                                          | Use for purchase invoices (accounts payable) or reimbursable employee expenses when the invoice will be paid later. Supported by all providers. |
| **[Expenses](/apis/accounting/reference#tag/Expenses)** (`POST /accounting/expenses`) | Represents a cash, credit‐card, or check purchase that is already paid. Includes fields like `transaction_date`, `account_id`, `supplier_id`, optional `payment_type`, and `line_items`. | Use for purchases or refunds already settled. Supported in **NetSuite**, **QuickBooks** and **Xero**, but not **Exact Online**.                 |

---

## Provider Mapping

| Provider         | Bills                          | Expenses                                                                            |
| ---------------- | ------------------------------ | ----------------------------------------------------------------------------------- |
| **NetSuite**     | Maps to **vendor bills**.      | Maps to **Expense Reports** (employee reimbursable expenses)                        |
| **QuickBooks**   | Maps to **vendor bills**.      | Maps to **Purchases** (cash/card expense transactions)                              |
| **Xero**         | Maps to **purchase bills**.    | Maps to **Bank Transactions** (spend money) — replaces deprecated expense-claim API |
| **Exact Online** | Maps to **purchase invoices**. | ❌ Not supported                                                                    |

---

## Expense Management Workflow Overview

Building a complete expense management solution requires several key components working together:

1. **Connect to accounting systems** via Apideck Vault
2. **Set up accounting mappings** for accounts, vendors, and categories
3. **Create expense transactions** using Bills or Expenses endpoints
4. **Upload receipts and attachments** for compliance
5. **Handle edge cases** like refunds, multi-currency, and booking periods
6. **Monitor via webhooks** for real-time synchronization

---

## Step-by-Step Integration

### 1. Configure Apideck and Obtain Credentials

1. Create an Apideck app in the dashboard. Follow the [get started guide](/get-started).
2. Implement Vault OAuth flow and store:

   - `consumer_id` (for `x-apideck-consumer-id` header)
   - `app_id` and `api_key`

3. If supporting multiple systems, pass a `serviceId` (`quickbooks`, `xero`, `netsuite`, `exact-online`, etc.) in the request.

---

### 2. Set Up Accounting Mappings

Before creating expense transactions, configure mappings between your expense categories and the customer's accounting system. This setup is typically done through your application's settings page.

#### Required Account Mappings

**Bank or Credit Card Accounts**

- Use [`GET /accounting/ledger-accounts`](/apis/accounting/reference#operation/ledgerAccountsAll) to fetch existing accounts
- Filter by `type=bank` or `type=credit_card` to show funding sources
- Allow users to select which account represents their expense card/account

**Expense Accounts (Chart of Accounts)**

- Fetch expense accounts using [`GET /accounting/ledger-accounts`](/apis/accounting/reference#operation/ledgerAccountsAll) with `type=expense`
- Map your expense categories (Travel, Meals, Office Supplies) to accounting expense accounts
- Essential for proper P&L categorization

**Vendors/Suppliers**

- Use [`GET /accounting/suppliers`](/apis/accounting/reference#operation/suppliersAll) to fetch existing vendors
- Allow creation of new vendors via [`POST /accounting/suppliers`](/apis/accounting/reference#operation/suppliersAdd)
- Map merchant names to proper accounting vendors for spend reporting

**Tax Rates** (for VAT/GST regions)

- Fetch available tax rates using [`GET /accounting/tax-rates`](/apis/accounting/reference#operation/taxRatesAll)
- Configure default tax rates for different expense categories
- Critical for businesses in VAT-regulated jurisdictions

#### Example: Fetching Chart of Accounts

```javascript
// Get expense accounts for mapping
const expenseAccounts = await apideck.accounting.ledgerAccountsAll({
  filter: { type: 'expense' },
  serviceId: 'exact-online'
})

// Display accounts for user mapping
expenseAccounts.data.forEach((account) => {
  console.log(`${account.name} (${account.code})`)
})
```

---

### 3. Build Expense Export Logic

#### Decision Flow

- **NetSuite**: Supports both **Bills** and **Expenses**
  - Use **Bills** for vendor invoices requiring approval workflow
  - Use **Expenses** for employee expense reports and reimbursements
  - Include departments, classes, and locations for comprehensive tracking
- **QuickBooks**:
  - Use **Expenses** for paid purchases
  - Use **Bills** for unpaid invoices
  - Consider adding Classes for departmental tracking
- **Xero**:
  - Use **Bank Transaction (Expense)**
  - **Bills** for unpaid invoices
  - Include tracking categories for enhanced expense categorization
- **Exact Online**: Use **Bill** only (Expenses not supported)
  - Include cost centers/cost units for expense categorization

#### Example: Creating a Bill

```json
{
  "id": "exp-12345",
  "bill_number": "INV-2025-0001",
  "supplier_id": "sup-789",
  "bill_date": "2025-07-31",
  "due_date": "2025-08-30",
  "currency": "EUR",
  "line_items": [
    {
      "account_id": "6000",
      "description": "Travel expenses",
      "total_amount": 250.0
    }
  ],
  "total": 250.0,
  "status": "draft"
}
```

> Required fields: `bill_date`, `due_date`, `line_items`, `total`, `status`.

---

#### Example: Creating an Expense

```json
{
  "transaction_date": "2025-07-31T12:00:00Z",
  "account_id": "6000",
  "supplier_id": "sup-123",
  "payment_type": "cash",
  "currency": "EUR",
  "line_items": [
    {
      "account_id": "6000",
      "description": "Lunch with client",
      "total_amount": 35.0
    }
  ],
  "total_amount": 35.0,
  "memo": "Team lunch",
  "type": "expense"
}
```

> Required fields: `transaction_date`, `account_id`, `line_items`. Optional: `supplier_id`, `customer_id`, `payment_type`, `memo`, `currency`.

---

### 4. Call Unified Endpoint

#### [Create a Bill](/apis/accounting/reference#operation/billsAdd)

```
POST https://unify.apideck.com/accounting/bills
Headers:
  x-apideck-consumer-id: {consumer_id}
  x-apideck-app-id: {app_id}
  Authorization: Bearer {api_key}
  Content-Type: application/json
```

#### [Create an Expense](/apis/accounting/reference#operation/expensesAdd)

```
POST https://unify.apideck.com/accounting/expenses
Headers: (same as above)
```

> If multiple connectors are enabled, include `serviceId=exact-online` (or others).

---

### 5. Upload Receipt Attachments

For compliance and audit purposes, attach receipts and supporting documents to expense transactions. Use the [Upload Attachment API](/apis/accounting/reference#operation/attachmentsUpload) directly since file uploads are not supported via the SDK:

```javascript
// Upload receipt after creating expense using HTTP API
const formData = new FormData()
formData.append('file', receiptFile)

const response = await fetch(
  `https://unify.apideck.com/accounting/attachments/expense/expense-123`,
  {
    method: 'POST',
    headers: {
      'x-apideck-consumer-id': consumerId,
      'x-apideck-app-id': appId,
      Authorization: `Bearer ${apiKey}`,
      'x-apideck-service-id': 'quickbooks', // or target provider
      'x-apideck-metadata': JSON.stringify({
        name: 'receipt.pdf',
        description: 'Expense receipt'
      })
    },
    body: formData
  }
)

const attachment = await response.json()
```

**Best Practices:**

- Support common formats: PDF, PNG, JPG
- Compress images to reduce upload time
- Store attachment IDs for future reference
- Handle upload failures gracefully

---

### 6. Configure Webhooks for Real-Time Updates

Set up [webhooks](/apis/webhook/reference) to monitor expense sync status and handle connection issues:

```javascript
// Monitor accounting.expense.created events
app.post('/webhooks/apideck', (req, res) => {
  const { event_type, payload } = req.body

  if (event_type === 'accounting.expense.created') {
    console.log('Expense synced:', payload.id)
    // Update expense status in your database
  }
})
```

**Key Webhook Events:**

- `accounting.expense.created` - Expense successfully synced
- `accounting.bill.created` - Bill successfully synced
- `vault.connection.callable` - Connection ready for API calls
- `vault.connection.invalid` - Connection requires re-authorization

---

## Tracking Categories for Enhanced Expense Categorization

For businesses that need to categorize expenses by departments, projects, locations, or other dimensions, **tracking categories** provide powerful categorization capabilities. Apideck's unified [Tracking Categories API](/apis/accounting/reference#tag/Tracking-Categories) abstracts the complexity of different provider implementations into a single, consistent interface.

**Key Benefit**: Instead of learning and implementing different tracking systems for each provider (Xero's tracking categories, QuickBooks' classes, NetSuite's dimensions, Exact's cost centers), you use one unified API that works identically across all platforms.

**Unified Approach**: Rather than handling provider-specific APIs, use Apideck's unified tracking categories endpoint to:

- **Retrieve** available tracking categories: [`GET /accounting/tracking-categories`](/apis/accounting/reference#operation/trackingCategoriesAll)
- **Create** new tracking categories: [`POST /accounting/tracking-categories`](/apis/accounting/reference#operation/trackingCategoriesAdd)
- **Apply** tracking categories to bills and expenses using the same data structure across all providers

> **Note**: For detailed guidance on location-based expense tracking across different accounting platforms, see our [Managing Locations, Departments, and Subsidiaries](/guides/locations-subsidiaries-departments) guide.

### How Providers Map to Unified Tracking Categories

**Xero**: Native tracking categories system

- Maps directly to Apideck's unified tracking categories
- Supports flexible categorization like "Department", "Project", or "Location"
- Allows both transaction-level and line-item tracking

#### Example: Unified Bill Structure (Works Across All Providers)

The same tracking category structure works whether you're sending to Xero, QuickBooks, NetSuite, or Exact Online:

```json
{
  "id": "exp-12345",
  "bill_number": "INV-2025-0001",
  "supplier_id": "sup-789",
  "bill_date": "2025-07-31",
  "due_date": "2025-08-30",
  "currency": "EUR",
  "line_items": [
    {
      "account_id": "6000",
      "description": "Travel expenses",
      "total_amount": 250.0,
      "tax_rate_id": "vat-20",
      "tracking_categories": [
        {
          "id": "dept-001",
          "name": "Sales Department"
        },
        {
          "id": "proj-456",
          "name": "Q1 Campaign"
        }
      ]
    }
  ],
  "total": 300.0,
  "sub_total": 250.0,
  "tax_amount": 50.0,
  "status": "draft"
}
```

**Behind the scenes**, Apideck automatically maps these tracking categories to:

- **Xero**: Native tracking categories
- **QuickBooks**: Classes
- **NetSuite**: Departments, Classes, or Locations
- **Exact Online**: Cost Centers or Cost Units

**QuickBooks**: Classes system

- Maps QuickBooks **Classes** to Apideck's tracking categories
- Track departments, locations, or profit centers
- Available on both bills and expenses

**NetSuite**: Multi-dimensional tracking system

- Maps **Departments**, **Classes**, and **Locations** to tracking categories
- Supports the most comprehensive tracking capabilities
- Includes subsidiaries, custom fields, and project-based tracking

**Exact Online**: Cost centers and cost units

- Maps **Cost Centers** and **Cost Units** to tracking categories
- Essential for departmental expense allocation and project tracking

### Working with Unified Tracking Categories

Use the same API calls regardless of the underlying provider:

```javascript
// Fetch available tracking categories for any provider
const trackingCategories = await apideck.accounting.trackingCategoriesAll({
  serviceId: 'xero' // or 'quickbooks', 'netsuite', 'exact-online'
})

// Apply to expenses using unified structure
const expenseWithTracking = {
  transaction_date: '2025-07-31T12:00:00Z',
  account_id: '6000',
  line_items: [
    {
      account_id: '6000',
      description: 'Business lunch',
      total_amount: 35.0,
      tracking_categories: [
        { id: 'dept-001', name: 'Sales Department' },
        { id: 'proj-456', name: 'Q1 Campaign' }
      ]
    }
  ]
}
```

---

## Edge Cases and Best Practices

### Accounting Period Restrictions

**Books Closing Dates**: Most accounting systems prevent modifications to transactions before the books closing date. Always check the current accounting period before creating expenses:

```javascript
// Check if expense date is after books closing date
const company = await apideck.accounting.companyInfoOne()
const closingDate = company.fiscal_year_end_month

if (expenseDate < closingDate) {
  throw new Error('Cannot create expenses before books closing date')
}
```

### Multi-Currency Handling

**Currency Conversion**: When expenses are in different currencies than the company's base currency:

```javascript
{
  "currency": "EUR",
  "exchange_rate": 1.12, // EUR to USD rate
  "total_amount": 250.00, // Amount in EUR
  "line_items": [{
    "account_id": "6000",
    "description": "Travel expenses",
    "total_amount": 250.00
  }]
}
```

### Refunds and Corrections

**Handling Refunds**: Create negative expense amounts or use credit memos:

```javascript
// Refund as negative expense
{
  "transaction_date": "2025-07-31T12:00:00Z",
  "account_id": "6000",
  "total_amount": -35.00, // Negative amount for refund
  "memo": "Refund: Cancelled dinner reservation",
  "type": "expense"
}
```

### Error Handling

**Connection Issues**: Always handle connection state validation:

```javascript
try {
  const expense = await apideck.accounting.expensesAdd(expenseData)
} catch (error) {
  if (error.code === 'CONNECTOR_AUTH_ERROR') {
    // Redirect user to re-authorize connection
    redirectToVault(consumerId)
  }
}
```

---

## Handling Provider Differences

- **NetSuite**: Supports both **Bills** and **Expenses**. Use Bills for vendor invoices and Expenses for employee expense reports. Supports comprehensive subsidiary, department, class, and location tracking.
- **QuickBooks**: Expenses map to **Purchases**. Payment type defines if it's a `Purchase` or `CardCharge`.
- **Xero**: Expenses map to **Bank Transactions**. Bills map to purchase bills. Expense-claim API is deprecated.
- **Exact Online**: Only **Bills** supported. Use `supplier` as employee or merchant. Set `due_date = transaction_date` if already paid.
- **Multi-currency**: Provide `currency` and `currency_rate` if different from company base.
- **Attachments**: Upload via the [Attachments API](/apis/accounting/reference#tag/Attachments):

```
POST /accounting/attachments/{reference_type}/{reference_id}
```

Use `bill` or `expense` for `reference_type`.

---

## Flowchart Overview

1. Initialise Apideck: Get API keys and Vault connection. Follow the [get started guide](/get-started).
2. Identify provider: NetSuite, QuickBooks, Xero, or Exact Online.
3. Select resource:
   - **NetSuite** → Bill or Expense (based on workflow requirements)
   - **QuickBooks/Xero** → Bill or Expense (based on transaction type)
   - **Exact Online** → Always Bill (Expenses not supported)
4. Call the appropriate unified endpoint.
5. Store returned ID/status. Handle errors gracefully.

![Bills and Expenses Flowchart](/guides/bills-expenses-flowchart.png)

---

## Conclusion

By following this guide, you can streamline your accounting integrations using Apideck's unified API. With just one integration, you can support **NetSuite**, **QuickBooks**, **Xero**, and **Exact Online**, handling purchase invoices, expenses, and bank transactions while maintaining a clean, standardized data model.

---

## Related Resources

**Apideck Documentation:**
- [Apideck Microsoft Business Central Connector Expenses and Journal Entries Overview](https://developers.apideck.com/guides/journal-entries-expenses-mbc)
- [Apideck Workday Connector Financial Management Overview](https://developers.apideck.com/guides/journal-entries-expenses-ledger-accounts-workday)
