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 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.


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


When to Use Bills vs. Expenses

ResourcePurposeWhen to Use
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 (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

ProviderBillsExpenses
NetSuiteMaps to vendor bills.Maps to Expense Reports (employee reimbursable expenses)
QuickBooksMaps to vendor bills.Maps to Purchases (cash/card expense transactions)
XeroMaps to purchase bills.Maps to Bank Transactions (spend money) — replaces deprecated expense-claim API
Exact OnlineMaps 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.

  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/accounts 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/accounts with type=expense
  • Map your expense categories (Travel, Meals, Office Supplies) to accounting expense accounts
  • Essential for proper P&L categorization

Vendors/Suppliers

Tax Rates (for VAT/GST regions)

  • Fetch available tax rates using GET /accounting/tax-rates
  • Configure default tax rates for different expense categories
  • Critical for businesses in VAT-regulated jurisdictions

Example: Fetching Chart of Accounts

// Get expense accounts for mapping
const expenseAccounts = await apideck.accounting.accountsAll({
  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

{
  "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

{
  "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

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

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 directly since file uploads are not supported via the SDK:

// 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
    },
    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 to monitor expense sync status and handle connection issues:

// 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 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:

Note: For detailed guidance on location-based expense tracking across different accounting platforms, see our Managing Locations, Departments, and Subsidiaries 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:

{
  "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:

// 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:

// Check if expense date is after books closing date
const company = await apideck.accounting.companiesOne()
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:

{
  "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:

// 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:

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:
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.
  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
Bills and Expenses Flowchart

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.