# Accounts Receivable Automation

Automating accounts receivable means creating customers in the downstream accounting system, pushing invoices against those customers, and reconciling payments back to the original invoices. This guide walks through the three-step flow using Apideck's unified [Accounting API](/apis/accounting/reference) and covers connector-specific behavior for the platforms that vertical SaaS and fintech products typically run into.

## Why a Unified API

Every accounting system models customers, invoices, and payments slightly differently. The field names diverge, the rules for what is computed versus writable diverge, and the way a payment is linked back to an invoice diverges. A single integration against Apideck's unified model handles those differences once.

- One contract for `customers`, `invoices`, and `payments` across the supported accounting connectors.
- Payment allocations let the downstream system flip invoice status to paid using its own rules.
- Vault handles auth, token refresh, and connector lifecycle.
- Webhooks emit `accounting.invoice.updated` and `accounting.payment.created` so reconciliation stays in sync without polling.

## Resource mapping

| Connector | Invoice maps to |
| --- | --- |
| QuickBooks | `Invoice` |
| Xero | `Invoice` (type `ACCREC`) |
| NetSuite | `Invoice` (transaction type) |
| Sage Intacct | `AR Invoice` |
| Exact Online | `Sales Invoice` |
| Stripe | `Invoice` (with auto-collected `PaymentIntent`) |
| Odoo | `account.move` (type `out_invoice`) |
| FreshBooks | `Invoice` |
| Zoho Books | `Invoice` |
| Workday | `Customer Invoice` |

## Walkthrough

The flow is the same for every connector that supports the full path: resolve the customer, create the invoice, then record a payment with an allocation back to that invoice.

### 1. Resolve or create the customer

Before pushing an invoice, you need a `customer.id` that exists in the downstream system. Search by email or display name first to avoid creating duplicates, then fall back to creating a new customer if no match is found.

```json
{
  "display_name": "Beachside Surf Co.",
  "company_name": "Beachside Surf Co. LLC",
  "email": "ap@beachsidesurf.com",
  "currency": "USD",
  "addresses": [
    {
      "type": "billing",
      "line1": "221 Pacific Ave",
      "city": "Santa Cruz",
      "state": "CA",
      "postal_code": "95060",
      "country": "US"
    }
  ],
  "phone_numbers": [
    {
      "number": "+1-831-555-0144",
      "type": "primary"
    }
  ],
  "payment_method": "credit_card"
}
```

Send this to [`POST /accounting/customers`](/apis/accounting/reference#operation/customersAdd) with the standard headers:

```http
POST /accounting/customers HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer ${APIDECK_API_KEY}
x-apideck-app-id: ${APIDECK_APP_ID}
x-apideck-consumer-id: cons_01H8X9Y2A3K4M5N6P7Q8R9S0T1
x-apideck-service-id: quickbooks
Content-Type: application/json
```

The response includes the connector-issued `id` you will reference on the invoice. On connectors where customers are read-only (see the table below), skip this step and look up the existing customer with [`GET /accounting/customers`](/apis/accounting/reference#operation/customersAll).

### 2. Push the invoice

Create the invoice against the resolved customer. The unified model expects line items with `description`, `quantity`, and `unit_price`. The downstream system will compute totals and tax based on the `tax_rate` you supply.

```json
{
  "invoice_number": "INV-2025-00184",
  "customer": {
    "id": "acct_01H8X9Y2A3K4M5N6P7Q8R9S0T1"
  },
  "invoice_date": "2025-03-04",
  "due_date": "2025-04-03",
  "currency": "USD",
  "reference": "PO-44219",
  "line_items": [
    {
      "type": "sales_item",
      "description": "Pro plan subscription, March 2025",
      "quantity": 1,
      "unit_price": 199.00,
      "total_amount": 199.00,
      "tax_rate": {
        "id": "TAX_NON"
      }
    },
    {
      "type": "sales_item",
      "description": "Onboarding hours",
      "quantity": 4,
      "unit_price": 150.00,
      "total_amount": 600.00,
      "tax_rate": {
        "id": "TAX_NON"
      }
    }
  ],
  "sub_total": 799.00,
  "total_amount": 799.00,
  "status": "authorised",
  "payment_method": "credit_card"
}
```

Send this to [`POST /accounting/invoices`](/apis/accounting/reference#operation/invoicesAdd). Capture the returned invoice `id`. That value is what you allocate the payment to in the next step.

### 3. Record the payment with an allocation

In most accounting systems, invoice status is computed from outstanding balance, not set directly. Trying to `PATCH` the invoice with `status: "paid"` will fail or be ignored. Allocate a payment instead, and the connector flips the status as part of the post.

```json
{
  "transaction_date": "2025-03-12T15:04:00.000Z",
  "currency": "USD",
  "total_amount": 799.00,
  "customer": {
    "id": "acct_01H8X9Y2A3K4M5N6P7Q8R9S0T1"
  },
  "payment_method": "Credit Card",
  "payment_method_reference": "ch_3OxPq2KJ8mZ4T1Lk0aN9bV2X",
  "accounts_receivable_account_type": "Account",
  "accounts_receivable_account_id": "1200",
  "type": "accounts_receivable",
  "reconciled": true,
  "status": "authorised",
  "allocations": [
    {
      "id": "inv_01H8YBZ3C4D5E6F7G8H9J0K1L2",
      "type": "invoice",
      "amount": 799.00
    }
  ],
  "note": "Stripe charge ch_3OxPq2KJ8mZ4T1Lk0aN9bV2X"
}
```

Send this to [`POST /accounting/payments`](/apis/accounting/reference#operation/paymentsAdd). The `allocations[].id` is the invoice ID returned in step 2, and `allocations[].amount` can be partial if you are recording a deposit. For a deeper breakdown of the allocation pattern, see the companion guide on [marking invoices and bills as paid](/guides/mark-invoices-as-paid).

### 4. Reconcile state via webhooks

Subscribe to `accounting.invoice.updated` and `accounting.payment.created` so your AR ledger stays in sync without polling. Use the invoice `balance` and `status` fields on the updated event to confirm the connector applied the allocation as expected.

## Connector-specific behavior

Coverage below reflects whether each connector supports creating the resource through Apideck. Read-only means the resource lists through `GET` but cannot be written, so you must reference an existing record from the downstream system. Verify any limited or partial coverage in the live coverage matrix before depending on it.

| Connector | Notes |
| --- | --- |
| Access Financials | Customers, invoices, and payments are all writable. Standard AR flow. |
| Acumatica | Customers, invoices, and payments are all writable. Standard AR flow. |
| Banqup | Customers and invoices are read-only and payments are not in coverage. Use this connector for ingesting AR data, not pushing it. |
| Campfire | Customers and invoices are writable. Payment writes are limited; verify allocations land before treating an invoice as settled. |
| Clear Books (UK) | Customers and invoices are read-only. Payments are not in coverage. Read-only AR sync only. |
| Digits | Customers are read-only. Invoices and payments are not in coverage. Reporting use only. |
| DualEntry | Customers, invoices, and payments are writable. Standard AR flow. |
| Exact Online | Customers are read-only. Look up the customer ID with `GET /accounting/customers` before invoicing. Invoices and payments are writable. |
| Exact Online (NL) | Same as Exact Online. Customers read-only, invoices and payments writable. |
| Exact Online (UK) | Customers are read-only. Invoices and payments are writable. |
| FreeAgent | Customers and invoices are writable. Payments are not in coverage; mark invoices as paid through the FreeAgent UI or by other means. |
| FreshBooks | Customers, invoices, and payments are all writable. Standard AR flow. |
| Intuit Enterprise Suite | Customers, invoices, and payments are all writable. Standard AR flow. |
| KashFlow | Customers and invoices are read-only. Payments are read-only. Use for reporting only. |
| Microsoft Dynamics 365 Business Central | Customers and invoices are writable. Payments are read-only and must be posted natively. |
| Moneybird | Customers and invoices are writable. Payment writes are limited; confirm allocations land before treating an invoice as settled. |
| MRI Software | Customers are read-only. Invoices and payments are not in coverage. |
| MYOB | Customers, invoices, and payments are all writable. Standard AR flow. |
| MYOB Acumatica | Customers, invoices, and payments are all writable. Standard AR flow. |
| NetSuite | Customers, invoices, and payments are all writable. Subsidiary is required on every record in OneWorld accounts. |
| Odoo | Customers, invoices, and payments are all writable. Invoices map to `account.move` with type `out_invoice`. |
| Pennylane | Customers and invoices are writable. Payments are not in coverage. |
| Procountor (FI) | Customers and invoices are writable. Payments are read-only. |
| QuickBooks Online | Customers, invoices, and payments are all writable. Standard AR flow. |
| Rillet | Customers, invoices, and payments are all writable. Standard AR flow. |
| Sage Business Cloud Accounting | Customers, invoices, and payments are all writable. Standard AR flow. |
| Sage Intacct | Customers, invoices, and payments are all writable. Dimensions and locations may be required depending on the entity setup. |
| Sage Intacct (REST) | The REST connector does not yet expose customers, invoices, or payments. Use the legacy Sage Intacct connector for AR automation. |
| Stripe | Customers and invoices are writable. Payments are read-only because Stripe collects them itself. Treat the Stripe-emitted payment as the source of truth. |
| Visma Netvisor | Customers, invoices, and payments are all writable. Standard AR flow. |
| Wave | Customers and invoices are writable. Payments are not in coverage. Apply payments inside Wave or via journal entries. |
| Workday | Customers, invoices, and payments are all writable. Standard AR flow. |
| Xero | Customers, invoices, and payments are all writable. Send invoices with `status: "AUTHORISED"` to make them payable. |
| Yuki | Customers and invoices are writable. Payments are not in coverage. |
| Zoho Books | Customers, invoices, and payments are all writable. Standard AR flow. |

### Stripe

Stripe is not a general ledger. Apideck exposes Stripe customers and invoices as writable so you can push billable items, but payment objects are read-only because Stripe charges the card itself and emits the resulting payment. For an AR automation product, the typical pattern is to create the invoice through Apideck, let Stripe collect, then mirror the resulting payment into your other accounting connector via [`POST /accounting/payments`](/apis/accounting/reference#operation/paymentsAdd).

### NetSuite

In a OneWorld account, every customer, invoice, and payment must reference a subsidiary. Resolve subsidiaries with [`GET /accounting/subsidiaries`](/apis/accounting/reference#operation/subsidiariesAll) and persist the chosen subsidiary against the Apideck consumer so you do not have to look it up per request.

### Exact Online

Customers are read-only across the Exact Online connectors (`exact-online`, `exact-online-nl`, `exact-online-uk`). Build a customer picker in your UI that calls [`GET /accounting/customers`](/apis/accounting/reference#operation/customersAll) and have the user select the matching record before you push an invoice.

### QuickBooks Online and Xero

Both flip invoice status to paid automatically when a payment with an `allocations[]` entry is posted. Do not attempt to update the invoice status directly. The connector will reject status writes against computed fields.

## Next steps

- [Mark invoices and bills as paid](/guides/mark-invoices-as-paid) for the deeper allocation pattern, including partial payments and credit notes.
- [Handling bills and expenses](/guides/expenses-bills) for the accounts payable counterpart of this flow.
- [Accounting API reference](/apis/accounting/reference) for the full list of operations and fields.
