# Procurement with the Accounting API

Procurement workflows in vertical SaaS typically span three steps: raise a purchase order with a supplier, convert it to a bill when goods or services are received, and track what is still owed. Apideck's [Accounting API](/apis/accounting/reference) exposes unified `purchase-orders`, `bills`, `bill-payments`, and `aged-creditors` resources so the same code path works across QuickBooks, Xero, NetSuite, Sage Intacct, and the rest of the connector catalog.

## Why a Unified API

Procurement is one of the most fragmented areas in accounting integrations: every system has its own PO numbering, line-item shape, tax handling, and approval state machine. A unified API lets the integration team skip most of that.

- No per-connector mapping for supplier, account, and tax-rate lookups.
- One PO and one Bill schema instead of a dozen vendor-specific shapes.
- A dedicated aged-payables report rather than reaggregating bills by hand.
- Connection lifecycle and reauth handled by Apideck Vault.

## Resource mapping

Procurement spans three unified resources. The table below shows what each one resolves to in the most common downstream systems.

| Connector | Purchase order | Bill | Outstanding payables |
| --- | --- | --- | --- |
| QuickBooks | `PurchaseOrder` | `Bill` | A/P aging report |
| Xero | `Purchase Order` | `Bill` (ACCPAY invoice) | Aged Payables report |
| NetSuite | `Purchase Order` | `Vendor Bill` | A/P aging report |
| Sage Intacct | `Purchasing Transaction` | `AP Bill` | Aged payables |
| Exact Online | `Purchase Order` | `Purchase Invoice` | Outstanding payables |
| Microsoft Dynamics 365 Business Central | `Purchase Order` | `Purchase Invoice` | Aged Accounts Payable |
| Odoo | `purchase.order` | `account.move` (vendor bill) | Aged Payable report |

## Walkthrough

### 1. Look up the supplier and expense account

Before raising a purchase order, resolve the supplier and the ledger account the line items will post to. Both are exposed as unified resources.

```json
{
  "filter": {
    "company_name": "Northwind Office Supplies"
  }
}
```

Send this to [`GET /accounting/suppliers`](/apis/accounting/reference#operation/suppliersAll) with the headers below. Use the same headers on every other call in this guide.

```
GET https://unify.apideck.com/accounting/suppliers
Authorization: Bearer <APIDECK_API_KEY>
x-apideck-app-id: app_FmF6u3KJgL4Wt4tcG5Xy
x-apideck-consumer-id: user_01H8X9Y2A3B4C5D6E7F8G9H0
x-apideck-service-id: quickbooks
```

Fetch the matching expense account from [`GET /accounting/ledger-accounts`](/apis/accounting/reference#operation/ledgerAccountsAll) using `filter[type]=expense`. Cache both IDs against the supplier in your own database so subsequent POs skip the lookup.

### 2. Create the purchase order

With supplier and account IDs in hand, raise the PO. Keep the line items close to what the buyer actually requested. Most downstream systems will copy these straight onto the bill in the next step.

```json
{
  "po_number": "PO-2025-00417",
  "supplier_id": "sup_01H8X9Y2A3B4C5D6E7F8G9H0",
  "issued_date": "2025-03-04",
  "delivery_date": "2025-03-18",
  "currency": "USD",
  "status": "open",
  "reference": "Q1 office refresh",
  "line_items": [
    {
      "description": "Ergonomic desk chair",
      "quantity": 6,
      "unit_price": 289.0,
      "total_amount": 1734.0,
      "ledger_account": {
        "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H1"
      }
    },
    {
      "description": "Standing desk converter",
      "quantity": 6,
      "unit_price": 145.0,
      "total_amount": 870.0,
      "ledger_account": {
        "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H1"
      }
    }
  ],
  "sub_total": 2604.0,
  "total_tax": 234.36,
  "total": 2838.36,
  "memo": "Net 30 terms, deliver to NYC office"
}
```

Send this to `POST /accounting/purchase-orders`. See [Purchase Orders create](/apis/accounting/reference#operation/purchaseOrdersAdd) for the full schema. Store the returned PO ID against your internal procurement record so it can be correlated with the bill later.

### 3. Convert the PO to a bill on receipt

When goods arrive (or services are confirmed), turn the PO into a bill. Carry the PO number through the bill's `reference` field so connectors and downstream users can correlate the two. For partial receipts, copy only the received line items and keep the PO open for the rest.

```json
{
  "bill_number": "NW-INV-9418",
  "supplier_id": "sup_01H8X9Y2A3B4C5D6E7F8G9H0",
  "bill_date": "2025-03-18",
  "due_date": "2025-04-17",
  "currency": "USD",
  "status": "authorised",
  "reference": "PO-2025-00417",
  "line_items": [
    {
      "description": "Ergonomic desk chair",
      "quantity": 6,
      "unit_price": 289.0,
      "total_amount": 1734.0,
      "ledger_account": {
        "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H1"
      }
    },
    {
      "description": "Standing desk converter",
      "quantity": 6,
      "unit_price": 145.0,
      "total_amount": 870.0,
      "ledger_account": {
        "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H1"
      }
    }
  ],
  "sub_total": 2604.0,
  "total_tax": 234.36,
  "total": 2838.36
}
```

Send this to `POST /accounting/bills`. See [Bills create](/apis/accounting/reference#operation/billsAdd). After this call, the PO is fulfilled (fully or partially) and a payable exists in the customer's books.

### 4. Pay the bill and close the loop

When the buyer pays, post a bill payment with an allocation that points back at the bill. The downstream system flips the bill status to paid for you.

```json
{
  "currency": "USD",
  "total_amount": 2838.36,
  "transaction_date": "2025-04-15T09:00:00.000Z",
  "payment_method": "ach",
  "reference": "ACH-2025-04-15-0099",
  "supplier": {
    "id": "sup_01H8X9Y2A3B4C5D6E7F8G9H0"
  },
  "account": {
    "id": "acct_01H8X9Y2A3B4C5D6E7F8G9H4"
  },
  "type": "accounts_payable",
  "status": "authorised",
  "reconciled": true,
  "allocations": [
    {
      "id": "bill_01H8X9Y2A3B4C5D6E7F8G9H3",
      "type": "bill",
      "amount": 2838.36
    }
  ]
}
```

Send this to `POST /accounting/bill-payments`. See [Bill Payments create](/apis/accounting/reference#operation/billPaymentsAdd). For deeper detail on the allocations contract, see the [Mark Invoices and Bills as Paid](/guides/mark-invoices-as-paid) guide.

### 5. Track outstanding payables

To show buyers what they still owe, call the dedicated aged creditors report rather than aggregating open bills. The downstream system applies its own rounding, partial-payment handling, and bucket boundaries, which raw bill data will not match exactly.

```
GET https://unify.apideck.com/accounting/aged-creditors?filter[report_as_of_date]=2025-04-30&filter[period_count]=4&filter[period_length]=30
```

See [Aged Creditors one](/apis/accounting/reference#operation/agedCreditorsOne). The response contains supplier-level totals bucketed by age. Drop into [`GET /accounting/bills`](/apis/accounting/reference#operation/billsAll) with `filter[status]=open` only when a buyer drills into a specific supplier and line-level detail is needed.

## Connector-specific behavior

Many connectors below support the unified `purchase-orders`, `bills`, `bill-payments`, and `aged-creditors` flow described above. Where a connector is read-oriented or has a known quirk worth flagging, the row calls it out.

| Connector | Notes |
| --- | --- |
| Access Financials | Standard mapping. No known quirks beyond the unified model. |
| Acumatica | Standard mapping. No known quirks beyond the unified model. |
| Banqup | Standard mapping. No known quirks beyond the unified model. |
| Campfire | Standard mapping. No known quirks beyond the unified model. |
| Clear Books | Standard mapping. No known quirks beyond the unified model. |
| Digits | Standard mapping. No known quirks beyond the unified model. |
| DualEntry | Standard mapping. No known quirks beyond the unified model. |
| Exact Online | Bills map to purchase invoices. Standard mapping otherwise. |
| Exact Online (NL) | Same data model as Exact Online. Standard mapping. |
| Exact Online (UK) | Same data model as Exact Online. Standard mapping. |
| FreeAgent | Standard mapping. No known quirks beyond the unified model. |
| FreshBooks | Standard mapping. No known quirks beyond the unified model. |
| Intuit Enterprise Suite | Standard mapping. No known quirks beyond the unified model. |
| KashFlow | Standard mapping. No known quirks beyond the unified model. |
| Microsoft Dynamics 365 Business Central | Purchase orders and posted purchase invoices are separate documents. Use the bill `reference` field to carry the PO number through. |
| Moneybird | Standard mapping. No known quirks beyond the unified model. |
| MRI Software | Standard mapping. No known quirks beyond the unified model. |
| MYOB | Standard mapping. No known quirks beyond the unified model. |
| MYOB Acumatica | Standard mapping. No known quirks beyond the unified model. |
| NetSuite | Bills map to vendor bills. Subsidiary, department, class, and location are exposed through `tracking_categories`; populate them when the customer runs a multi-subsidiary book. |
| Odoo | Bills map to `account.move` records of vendor-bill type. Standard mapping otherwise. |
| Pennylane | Standard mapping. No known quirks beyond the unified model. |
| Procountor | Standard mapping. No known quirks beyond the unified model. |
| QuickBooks | Bills map to `Bill`, payments map to `BillPayment`. Use `tracking_categories` for QuickBooks Classes. |
| Rillet | Standard mapping. No known quirks beyond the unified model. |
| Sage Business Cloud Accounting | Standard mapping. No known quirks beyond the unified model. |
| Sage Intacct | Bills map to AP bills. Dimensions (department, location, class, project) flow through `tracking_categories`. |
| Sage Intacct (REST) | Same data model as Sage Intacct. Standard mapping. |
| Stripe | Stripe is a payments platform rather than a procurement system. Treat this connector as payable-visibility only. |
| Visma Netvisor | Standard mapping. No known quirks beyond the unified model. |
| Wave | Standard mapping. No known quirks beyond the unified model. |
| Workday | Standard mapping. No known quirks beyond the unified model. |
| Xero | Bills map to ACCPAY invoices. Use `tracking_categories` for Xero's native tracking. The Aged Payables report mirrors Xero's own bucketing. |
| Yuki | Standard mapping. No known quirks beyond the unified model. |
| Zoho Books | Standard mapping. No known quirks beyond the unified model. |

### NetSuite

For multi-subsidiary tenants, every PO and bill must carry a subsidiary. Resolve it via tracking categories at PO creation time so the downstream bill inherits it cleanly. See [Managing Locations, Departments, and Subsidiaries](/guides/locations-subsidiaries-departments) for the full pattern.

### Microsoft Dynamics 365 Business Central

Business Central distinguishes between the purchase order and the posted purchase invoice. The unified flow stays the same, and the bill becomes visible in the aged creditors report once the invoice has been posted in the customer's tenant.

## Next steps

- [Handling Bills and Expenses](/guides/expenses-bills) for the broader payables story including expense reports and receipts.
- [Mark Invoices and Bills as Paid](/guides/mark-invoices-as-paid) for the allocations contract on bill payments.
- [Accounting API reference](/apis/accounting/reference) for the full schema of every resource used above.
