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.
- 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
- Open
- Key
- Match to PO
- Code to GL
- File
- Review
- Ready to post
What you need
| Service | What it does | What to set up |
|---|---|---|
| Shared inbox | Receives supplier invoices | Any Google Workspace or Microsoft 365 mailbox n8n can read |
| n8n | Runs the workflow visually | n8n Cloud, or the free self-hosted edition |
| Gemini API | Reads each invoice | A Google AI Studio key with billing enabled, so your invoices are not used to improve Google’s products |
| NetSuite | Looks up open POs; receives approved bills | Enable 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 folder | Stores every invoice | Google Drive or SharePoint |
| Slack | Alerts the reviewer | Any 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
-
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.
-
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.
-
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.
-
Code it to the GL
The GL account is suggested from the PO or, without one, from how that supplier has been coded before.
-
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. -
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
- Read-only access to NetSuite until you sign off.
- Only exact matches go through. Everything else goes to a person, with a Slack alert.
- Nothing gets paid or posted without your review.