Guides / Accounts payable / NetSuite

How to cut weekly AP from 3 hours to 10 minutes in NetSuite

The AI-assisted accounts payable workflow we run for a small finance team on NetSuite: what it automates, where the human review sits, and how to set it up.

Michelle Chan, CA (Singapore) · · 4 min read

Before
3 hours a week opening, keying, matching POs and coding supplier invoices.
After
10 minutes a week, reviewing what the workflow has prepared.

Who this is for

Finance teams of 1–8 people who still key supplier invoices by hand. This example runs on NetSuite and handles about 30 invoices a week.

What gets automated

  1. Open
  2. Key
  3. Match to PO
  4. Code to GL
  5. File
  6. Review
  7. Ready to post

What you need

ServiceWhat it doesWhat to set up
Shared inboxReceives supplier invoicesAny Google Workspace or Microsoft 365 mailbox n8n can read
n8nRuns the workflow visuallyn8n Cloud, or the free self-hosted edition
Gemini APIReads each invoiceA Google AI Studio key with billing enabled, so your invoices are not used to improve Google’s products
NetSuiteLooks up open POs; receives approved billsEnable REST Web Services and Token-Based Authentication, create an integration record and a role that can view purchase orders and run SuiteQL. Start read-only.
Shared folderStores every invoiceGoogle Drive or SharePoint
SlackAlerts the reviewerAny plan

No API access? Schedule a NetSuite saved search of open POs to email a CSV to the AP inbox each morning, and match against that. It is simpler to set up, but PO data can be up to a day old.

Check what’s native first. NetSuite has its own Bill Capture feature for reading invoices. Availability depends on your account and region, so check whether it’s enabled before you build.

Using Xero instead? Read the Xero version.

Step by step

  1. Send every invoice to one place

    Suppliers email invoices to a shared AP inbox. n8n watches the inbox and picks up each new invoice as it arrives.

  2. Open and key the invoice

    n8n sends the invoice to the Gemini API with the prompt below. It returns the supplier, invoice number, dates, PO number, GST, totals and every line item as structured data.

  3. Match it to the purchase order

    The workflow looks up open POs in NetSuite. An invoice matches when it quotes the PO number, or when the vendor name, line item and amount all match exactly. If the amounts don’t match exactly, the invoice goes to an unmatched folder and a Slack message asks someone to review it.

  4. Code it to the GL

    The GL account is suggested from the PO or, without one, from how that supplier has been coded before.

  5. File it

    Each invoice is saved to the shared folder as Month_VendorName_InvoiceNumber, so anyone can find it by searching for the month, the supplier or the invoice number.

  6. Review, then post

    A person reviews every prepared invoice before anything is posted or paid. Anything the workflow is unsure about is flagged for you instead of guessed.

A sample extraction prompt

Adapt this to your invoices. Ask the model to return JSON only, so n8n can read the result directly.

You are an accounts payable assistant. Extract the data from the
supplier invoice provided and return JSON only, using the schema below.

Rules:
- Copy values exactly as printed. Do not calculate, infer or guess.
- If a field is missing, unreadable or ambiguous, set it to null and
  add the field name to "needs_review".
- Dates as YYYY-MM-DD. Amounts as plain numbers, no currency symbols.
- List every line item separately. Do not merge lines.
- If subtotal + tax_amount does not equal total, add "total_mismatch"
  to "needs_review".
- If the document is not an invoice (a statement, reminder or credit
  note), set "document_type" to match and leave the other fields null.

Schema:
{
  "document_type": "invoice | credit_note | statement | other",
  "vendor_name": "string",
  "vendor_gst_number": "string or null",
  "invoice_number": "string",
  "invoice_date": "YYYY-MM-DD",
  "due_date": "YYYY-MM-DD or null",
  "po_number": "string or null",
  "currency": "ISO code, e.g. SGD",
  "subtotal": 0,
  "tax_amount": 0,
  "total": 0,
  "line_items": [
    { "description": "string", "quantity": 0, "unit_price": 0, "amount": 0 }
  ],
  "needs_review": []
}

Where the controls sit