White River Hardwoods · AI Team · From a Thought to Reality

The Starter
Kit.

The companion to Where to Start. Everything you need to climb the ladder one rung at a time: the product-page field guide with its prompts, three working n8n flows ready to import, the inbox-assistant pattern, the hook into your system of record, and what to have in place before you ever build an agent.

Day zero · Pick the AI partner you'll build with
Part 1 · Product pages a machine can read
Part 2 · Two flows that show up on Monday — and the Shopify custom app
Part 3 · The assistant that never sends
Part 4 · Wire the agents to the system of record
Part 5 · Before the agents
Version 2 · Share freely · whiteriver.com/pages/ai-talk · Built with Claude (Anthropic)
How to use this

Read the short one first. Then open this.

The handout — Where to Start — is the argument, and its second page is the map. This is the toolbox. It's organized as five parts that follow the ladder, and it's meant to be used in order, because each part assumes you've done the one before it. You can skip ahead. I wouldn't.

Day zero · before rung one

Pick the AI partner you'll build with.

Everything in this kit was built by one person describing a problem to an AI and testing what came back. So the first decision isn't a tool or a platform — it's which AI you'll do that with. Claude, or ChatGPT with code execution. Both can read a product export, write a workflow, and explain an error. Pick the one you find yourself working best with and stay with it; switching mid-build means re-explaining your business every time.

This isn't rocket science. You don't need to know what a webhook is on day zero — I didn't. You need to describe what you want plainly, answer the questions that come back (there will be a lot in the first hour), and test what you're handed before you trust it. The partner writes the code. You make the calls. That's the whole skill, and it compounds.

What's in it

  • Part 1 is the full field guide for rebuilding product pages so Google and AI assistants can read them — eight steps, four copy-paste prompts, the schema block, and the mistakes we made. Nothing to buy.
  • Part 2 is two of our n8n workflows, sanitized and ready to import, each with a setup card — plus the Shopify custom app that lets a flow see your store. These are the ones that broke the fear for our team: a report that just shows up.
  • Part 3 is the pattern for an inbox assistant that drafts replies in a rep's voice and never sends. No file to import — the design, the rules, and the one node that makes it safe.
  • Part 4 is the hook into your system of record — how we gave the agents a read-only line into NetSuite for stock, lead times, order status and tracking, in one afternoon, without buying a seat. A third flow to import, and the field notes.
  • Part 5 is what to have true before you put an AI on the phone. Not how to build agents; what has to already be working.

What you need on hand

  • An AI assistant that can read files and run code — Claude, or ChatGPT with code execution (day zero, and all parts)
  • Your store admin and a product export (Part 1)
  • An n8n account — cloud is fine, about $20–60 a month (Part 2)
  • An Anthropic API key and a Perplexity API key (Part 2 — both have free tiers to start)
  • A transactional email account — we use Resend; any SMTP works (Part 2)
  • A Shopify custom app — free, ten minutes, in your store admin (Part 2, extended in Part 3)
  • Admin access to your ERP for one afternoon, and the person who owns it (Part 4)
  • Someone who knows the products and can answer questions (all parts, and the most important one)
One rule runs through every part. A page, a report, a draft, or an agent can only say what you can prove. The AI organizes facts; it doesn't invent them. When it doesn't have a fact, the right output is nothing, and the right next step is a question to a person. Every mistake in this kit came from breaking that rule. Every fix was going back to it.
Part one · Rung one

Product pages a
machine can read.

Nobody sees it. It costs nothing. It can't break anything. And it's where you learn the rhythm every later rung uses: audit, decide the rules, template, generate, check. We did 7,754 pages this way across three storefronts. Here is exactly how.

01 · Step oneExport your catalog and audit it before touching anything

Export every product as a CSV. Then hand the file to your AI assistant and ask it to audit — not to write anything yet. You are looking for the gap between what your pages say and what your data says.

What a good audit finds

  • How many products have no SEO title or description (we had 3,796 of 3,849 on one site)
  • How many share identical body copy — one boilerplate paragraph pasted across a whole category
  • Facts trapped in the wrong field — lead times typed into SKUs, dimensions only in the title, "sold 2 per card" in a parenthetical
  • Duplicate products — same title, same SKU, two pages
  • Products with $0 prices, empty bodies, or old page-builder markup
  • Claims the pages make that you can't back up

What we found on ours

  • Four of five acoustic trim pages described the wrong finish — all said "White Oak"
  • 19 products priced at $0 and active
  • One site's schema code told Google its products lived on a sister site
  • 704 pages quoting a 35% trade discount that was actually 38%
  • A product handle with an internal note in it: …-the-bevel-is-different-from-the-201-profile
  • Panel moulding pages listing sizes and species that didn't exist as variants
Prompt · AuditHere is a full product export from my store. Before writing anything, audit it and tell me: how many products have no SEO title or description; how many share identical body text; which products have $0 prices, empty bodies, or duplicate titles; and any facts that appear to be trapped in titles, SKUs, or tags rather than in their own fields. Group everything by product type. Flag anything that looks like a live error on the site. Do not generate any content yet.

02 · Step twoDecide the rules before writing a word

The AI will ask questions. Answer them all before it generates anything, because every answer changes thousands of pages. These are the ones that came up for us; most businesses have a version of each.

DecisionWhy it mattersOur answer
Whose voice?Brand name in the header, the contact lines, the closingEach site speaks as itself; one "made by" credit
Discounts on the page?Numbers go stale; some lines are net-pricedPercent on one site, none on the distributor site, never on European carvings
Country of origin?A false "Made in USA" is a legal problem; silence is safeClaim it only where we manufacture; say nothing on imported lines
Phone number?Changes when support moves to AI or a new lineOn one site only
What is that option?"Resin" costs 4× "Poplar" and the page didn't say whyFlexible, bends for curves, interior/exterior
Overwrite existing pages?Some were hand-done — and some were wrongYes: consistency beats keeping 704 wrong pages
Unpublished / empty products?Skip, include, or draft themBuild them; draft the ones with $0 prices

Write the rules down and give them to the AI as a single list. Ours was a canonical table of discounts by product line, a list of which lines are domestic, the trade URL and phone per site, and the lines that must never show a trade offer. Every later prompt referred back to it. When two sources disagreed — a project note said 35%, the fleet sync said 13% — we stopped and asked instead of picking one.

03 · Step threeBuild one page template per product family

Do not write 3,000 pages. Write one template for each family of products that shares a shape — crown mouldings, medallions, corbels — and let the data fill it in. A family is anything where the same questions get asked: what size, what material, what does it fit, how is it sold, how do I install it.

Every template has the same skeleton. Machines like consistency; so do customers.

HeroProduct name as an H1 · SKU · the two or three numbers that define it · one honest sentence Spec tableA real HTML table: sizes, species, finishes, prices per variant. This is the single most valuable block on the page. Feature cardsFour short reasons this product is what it is — material, construction, use, what it pairs with Key FactsLabel/value pairs: SKU, dimensions, material, lead time, minimum order, made in, fits. Nine to fifteen. InstallationNumbered steps, each with a heading. Specific to this family, not generic. RelatedLinks to what goes with it — the matching corner block, the parent profile, the component parts Trade / CTAOne call to action, where the rules allow it Care · ClosingTwo lines of care; a one-line closing with the SKU

Three rules for the copy

Prompt · TemplateHere are 20 products from my "[family]" category with their titles, variants, tags, and current descriptions, plus my rules list. Design one page template for this family using this skeleton: hero, spec table, four feature cards, Key Facts grid, installation steps, related products, trade CTA, care, closing. Show me which fields on each product would fill each part of the template, and which facts you would need from me that the data doesn't contain. Render one example so I can review it before you build the rest.

04 · Step fourGenerate from your data, not by hand

Once a template is approved, the AI writes a small program that reads each product's row and fills the template: the name from the title, dimensions parsed from the title, species and prices from the variants, lead times and fit specs from tags. The prose for the family was written once; the numbers on each page are that product's own.

This is the part that makes the whole thing cheap. A price change, a new profile, a rebrand, a second storefront with the same products — export, re-run, import. We built WhiteRiver.com's 3,813 pages in about a minute after Mouldings.com's had taken an afternoon, because the generators didn't care which store the row came from.

Small catalog (under ~100 products)

You don't need code. Give the AI the template and the products in batches of 20 and have it write the pages directly into a spreadsheet column. Review each batch. Import.

Larger catalog

Ask the AI to write the generator and run it. Insist on a spot-check step: it should render ten random products from each family and show you the H1, the parsed dimensions, and the spec grid before generating everything. Every parsing bug we hit — a fraction read wrong, a name doubled, a "fits" spec mistaken for a dimension — showed up in a spot-check.

Insist on validation output. After each family, have the AI report: products built, errors, pages with unbalanced HTML, pages with fewer than three headings, pages where a null value leaked into text, SEO titles over 70 characters, descriptions over 160. It should also check your rules: no trade offer on lines that shouldn't have one, no origin claim where none is allowed, no old discount number anywhere. Ours printed "validation: clean" at the end of every run, and when it didn't, we fixed it before importing.
Prompt · GenerateUsing the approved template for [family], generate a page for every product in that category from the export. Parse dimensions from the title and species and prices from the variants. Then: (1) show me ten random products with their H1, parsed dimensions, and Key Facts so I can check the parsing; (2) report any product where parsing failed; (3) validate every page for balanced HTML, at least three H2s, no leaked nulls, and my rules (list them). Output a CSV with the same columns as my export, changing only Body (HTML), SEO Title, and SEO Description.

05 · Step fiveWrite the SEO title and description from a formula

These two fields are what shows in search results and what AI tools quote first. Most small catalogs have them blank. Don't write them by hand; give the AI a formula and let it fill it per product.

Title (under 70 characters)

[Name] [Product type] | [Material or key attribute] | [Brand]

Foster Traditional Crown Moulding | Urethane | White River Hardwoods

When 500 products share the same generic name, differentiate with the number that matters: 14-3/4″ Urethane Ceiling Medallion MED1131-C | One-Piece.

Description (under 155 characters)

[Name] [type], [dimensions]. [Two facts a buyer wants]. [One trust line].

Foster Traditional urethane crown moulding, 6-3/4″ H × 7″ P × 94-1/2″ L. Factory primed, made in USA, interior or exterior. Trade pricing available.

Leave prices out. They change; the description doesn't get updated when they do. Every stale price we found was in a description.

06 · Step sixAdd the structured data block

This is the "invisible code" part. A JSON-LD block tells any machine: this is a Product, here is its name, SKU, image, price, availability, brand. It goes either at the bottom of the product description or in your theme's product template. Theme is cleaner (one edit, every product); description is easier if you're already regenerating pages. Don't do both — a duplicate block is worse than one.

// Paste at the bottom of the product description, or in the theme's product template
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "PRODUCT NAME",
  "url": "https://yourstore.com/products/HANDLE",
  "sku": "SKU",
  "brand": { "@type": "Brand", "name": "YOUR BRAND" },
  "image": "https://…/main-image.jpg",
  "description": "THE SEO DESCRIPTION",
  "material": "MATERIAL",
  "countryOfOrigin": "US",            // only if the page says so
  "offers": {
    "@type": "Offer",                 // use "AggregateOffer" + lowPrice/highPrice for multi-variant
    "priceCurrency": "USD",
    "price": "118.57",
    "availability": "https://schema.org/InStock"   // or PreOrder for made-to-order
  }
}
</script>

The schema follows the same rules as the copy. If the page doesn't claim a country of origin, the block doesn't either. If a line is net-priced, nothing in the block implies a discount. And check the url: one of our sites had schema copied from another site, telling Google the products lived on a different domain. A machine-readable claim is still a claim — and a wrong one is worse than none.

Prompt · SchemaFor every generated page, append a schema.org Product JSON-LD block using the product's name (from the H1), handle, first SKU, first image, SEO description, and material from the Key Facts. Use Offer for single-price products and AggregateOffer with lowPrice/highPrice for multi-variant. Use InStock, or PreOrder for made-to-order items. Include countryOfOrigin only when the page states a country. Validate that every block parses as JSON and that every url is on my domain.

07 · Step sevenImport safely

08 · Step eightVerify — and then ask an AI about your own product

What the test looks like — two real results, same site, same night

A regenerated page. Every row green. "Non-critical" means Google would like optional fields — a rating, shipping details, a return policy — but nothing blocks the rich result.

A page we hadn't regenerated yet. The Product block passed the basic check but failed the stricter Merchant listing rules; the two red review items came from a reviews app emitting its own schema with a required field missing. Neither is a disaster — but you only find out by running the test.

Read the rows, not the headline. "Some are invalid" sounds worse than it is. One Product block is evaluated against several result types at once, and third-party apps add their own blocks you didn't write. Click each red row: the message names the exact missing field. Fix what's yours; send the rest to the app vendor.
Things that will go wrong

Mistakes we made, so you don't have to

What happenedWhyThe fix
A corner block linked to the wrong crownWe matched on the SKU prefix; the suffix letter was a different sizeMatch on the exact SKU. Always spot-check cross-links.
"Pediment Pediment" in a headingThe product name already contained the type wordStrip the type word before composing the heading; check ten random H1s per family.
A capital's width came out as 16″The title said "(Fits Pilasters up to 16″W)" before the real dimensionsRemove parentheticals before parsing dimensions; put "fits" in its own field.
A 34-foot medallionThe title had a foot mark where it meant inchesLet the page show nothing rather than a wrong number; flag it for the data owner.
Lead time inside the SKU fieldSomeone typed "SHIPS: 6-10 BUSINESS DAYS" after the SKUDisplay the clean SKU, pull the lead time into a field — and fix the source, because your ERP sees that field too.
A resin option described as "a cast version of the same profile"We didn't know what it was, so the AI wrote something safe and uselessAsk. It was flexible, curved-wall, exterior-rated — a real reason to pay 4×. Then patch the pages already shipped.
A promo band with the successor product right in itTold the customer "buy this other thing" at the moment they saw the discountDecide where an upsell belongs before it ships. Bottom of the page, usually.

The pattern behind every one of these: the data was ambiguous and the AI resolved the ambiguity by itself. The fix each time was a human answer, a spot-check, or both. Budget time for questions. The AI should ask more than it assumes, and you should expect to answer a lot in the first hour and very little after.

The one-page checklist

  • Export the full catalog; keep the original
  • Audit first — no writing until the audit is read
  • Write the rules list: voice, discounts, origin, contact, what each option means
  • Group products into families that share a shape
  • Build one template per family; render one example; approve it
  • Generate from data; spot-check ten per family; read the validation
  • SEO title <70 chars, description <155, no prices
  • Schema block on every page, in theme or body, never both
  • Import files touch only the intended columns
  • Split under 12 MB; test one product; one file at a time
  • Rich Results test, view source, ask an AI about your product
  • Send the data owner the list of things the audit found
Part two · Rung two

Two flows that
show up on Monday.

These are real workflows from our n8n, with our company, our competitors, our addresses and our API key stripped out and a configuration node put in their place. Import one, fill in five fields, run it in test mode, read the email. That's the whole first week.

Before you import either flow

Ten minutes of setup.

1. Get the two keys

Anthropic — console.anthropic.com → API Keys. This is the model that reads and writes. Perplexity — perplexity.ai → API. This is the tool that searches the web. Both take a card; both cost cents per run at this volume.

2. Set up sending

We use Resend (resend.com): verify a domain, create an API key. In n8n go to Settings → Variables and add RESEND_API_KEY with that value. The flows read it from there so the key never sits inside a workflow. Don't have Resend? Delete the "Send via Resend" node and drop in n8n's Gmail or SMTP node — the HTML is in {{ $json.html }}.

3. Import

In n8n: Workflows → ⋯ → Import from File, pick the JSON. It opens with a sticky note on the canvas that repeats these instructions.

4. Connect credentials

Click the Anthropic Chat Model node → Credential → create with your key. Same for the Perplexity node. Red warning triangles on nodes mean a credential is missing.

5. Test, then turn it on

Every flow ships with testMode = true, which sends only to testEmail. Fill in Workflow Configuration, click Execute Workflow, read what arrives. When it's right, set testMode to false and toggle the workflow Active.

Why the key lives in a variable, and the lesson behind it. When I exported these flows to share them, the Resend key was hardcoded in the send node — in plain text, in a file that was about to leave the building. We caught it, stripped it, and rotated the key. The kit versions read the key from n8n's variables instead. If you ever export a workflow to share, search the file for your keys first. Twice.

Flow A — Weekly Competitive Review

File: Starter_Weekly_Competitive_Review.json · 11 nodes · Runs Mondays 9am · Uses AI: yes (research + summary)
Download the flow

Every Monday it researches each company on your list — yours first — with one focused web search apiece, then combines the findings into a single branded email with your company's card highlighted at the top. Our team reads it before the week starts and nobody ran a search.

The five fields to change · Workflow Configuration node
companiesA list. Your company first — the flow treats the first entry as the benchmark. Then 3–5 competitors. More than five and the email gets long.
competitorWebsitesOne URL per name, spelled exactly as in companies. The agent starts each search from the site.
industryOne phrase — "decorative wood wall panels," "commercial HVAC service," whatever you sell. Every search is scoped to it, which is what keeps the results on topic.
emailRecipient · fromAddressComma-separated recipients; a verified sender.
testMode · testEmailShips on. Turn off after the first read.

What each node does

1Weekly ScheduleFires Monday at 9. Change day/hour here; the flow doesn't care what day it is.
2Workflow ConfigurationEvery setting in one place. Nothing else in the flow needs editing.
3Split Out CompaniesTurns the list into one item per company so each gets its own search.
4Prepare Company ListAttaches each company's website from the config.
5Research AgentClaude, with Perplexity as its only tool. The system prompt tells it to research ONE company, stay inside your industry, and never mention the others. Retries 4× if the model is busy.
6Combine All ReportsWaits for all companies, then gathers the reports into one item.
7Format Final SummaryPlain JavaScript, no AI. Builds the HTML email, sorts your company first, drops off-topic results, adds the date range. Brand colors are at the top of the code — swap them for yours.
8Send via ResendPosts the email. Reads the key from $vars.RESEND_API_KEY.
The lesson this flow teaches: the agent does one narrow job — research one company inside one industry — and a dumb code node does the assembly. When we let the agent write the whole email, it drifted; when we let it research four companies at once, it blurred them. One search, one company, one report, then code. That pattern shows up in every flow we've built since.

Flow B — Industry Trend Review

File: Starter_Industry_Trend_Review.json · 8 nodes · Runs Mon/Wed/Fri 7am · Uses AI: yes (three searches, JSON output)
Download the flow

Three mornings a week it sends the team a short newsletter: upcoming events from the trade associations your people attend, in the states that matter to you, plus two trend questions about your category — each trend with a source link. The agent returns strict JSON; a code node turns it into the email. No AI touches the formatting.

The fields to change · Workflow Configuration node
companyName · industryWho you are; what you sell, in a phrase.
associationsThe trade groups whose chapter events your team would actually go to — "ASID and NKBA" for us. Use yours.
statesWhere those events matter. Five is plenty.
trendTopicA · trendTopicBTwo questions the agent researches each run. Ours: trends in mouldings and millwork; where tambour panels are showing up. Make them specific to what your customers ask about.
emailRecipient · fromAddress · testModeSame as Flow A.

What each node does

1Schedule TriggerCron 0 7 * * 1,3,5 — 7am Mon/Wed/Fri. Change the days there.
2Workflow ConfigurationAll settings. Also stamps today's date for the agent.
3Trend AgentClaude + Perplexity. Runs three searches — events, trend A, trend B — and must return one JSON object in an exact shape. The prompt forbids inventing URLs: every source must be one Perplexity returned, and an empty list beats a made-up link.
4CodeFinds the JSON in the agent's output, parses it, and builds the branded email — summary, events table, trends with clickable source domains. Pure formatting.
5Send via ResendSame send node as Flow A.
The lesson this flow teaches: make the agent return data, not prose. Asking for a JSON object with a fixed shape — and parsing it with code — is what turns a chatty model into a reliable component. The one time we let it return free text, the email formatting broke a different way every run. And the anti-hallucination line in the prompt exists because a rep clicked a source link that went nowhere. Once.

What it looks like when it's working — and when it isn't

You seeIt meansDo
Email arrives, sections filled, links resolveWorkingTurn off testMode, activate
Red triangle on a model or Perplexity nodeCredential not connectedClick the node → Credential → select yours
"Overloaded" or 529 in the Research AgentModel busyNothing — the retry handles it. If it fails four times, run again later
Email arrives with "No content was generated"Agent output wasn't parseableOpen the agent node's output; usually a prompt edit broke the JSON shape
Results about the wrong industryindustry field too vagueMake it a phrase a stranger would understand
Send node errors with 401RESEND_API_KEY variable missing or wrongSettings → Variables; the value is the whole key
Nothing arrives, no errortestMode on and testEmail wrong, or the workflow isn't ActiveCheck both
Rung two plumbing · the piece neither document covered

The Shopify custom app, in ten minutes.

Every flow that touches your store — the sync checker, order status, draft orders, trade sign-ups — needs a key that lets n8n talk to Shopify. That key comes from a custom app you create yourself in the store admin. No developer account, no App Store, no cost. One per store; we have three.

Create it

1
Shopify admin → Settings → Apps and sales channels → Develop apps → Create an app. Name it for what it does, not who built it: "n8n Integration."
2
Configure Admin API scopes. Start read-only (table below). You can add scopes later; you cannot un-know a token that had too many.
3
Install app. The Admin API access token appears once. Copy it into your password manager before you click anything else.
4
In n8n: Credentials → Shopify Access Token (or Header Auth with X-Shopify-Access-Token). Reference it from the Shopify node or an HTTP Request node. Never paste the token into a workflow.
5
Test with the smallest possible read: list the last five orders. If that works, everything else will.

What it powers for us

  • createDraftOrder — every quote the voice agents build lands in Shopify as a draft order with trade pricing applied.
  • submitTradeApplication — trade sign-ups taken on the phone create the customer record and tag it for review.
  • Catalog and lead capture — read the customer, write the tag.
  • The sync checker — a daily comparison of Shopify orders against the ERP. No AI in it. Highest-leverage flow we run.
Where ours ended up · WhiteRiver.com, after a year
read_draft_orders, read_orders, read_customers, read_companies, read_fulfillments, read_shipping, read_analytics, read_price_rules, write_price_rules, write_draft_orders, write_marketing_events, read_marketing_events, write_payment_terms, read_payment_terms, read_metaobjects, write_metaobjects, read_product_listings, write_product_feeds, read_product_feeds, read_products, write_products, write_product_listings, write_customers, write_orders, write_shipping, write_order_edits, read_order_edits, read_assigned_fulfillment_orders, write_discounts, read_discounts, read_inventory, read_locations

Thirty-two scopes. Don't start here. Each one was added the week a flow needed it, and each addition meant reinstalling the app and rotating the token. That's the right friction.

Things that bit us

  • The token shows once. We regenerated ours twice before learning to copy first.
  • Adding a scope doesn't take effect until you reinstall — and reinstalling issues a new token. Update n8n the same minute.
  • Pin the API version in HTTP calls (/admin/api/2024-10/…). Unpinned calls break on Shopify's quarterly release.
  • Three stores means three apps and three credentials. Name them so Monday-morning you can tell them apart.

Grow it with the ladder

RungAdd these scopesSo that
2read_orders · read_products · read_customers · read_inventory · read_fulfillments · read_draft_ordersFlows can see orders, catalog and stock. The sync checker lives here.
3write_draft_orders · write_customersThe inbox assistant and the quote agents can create draft orders and trade accounts.
4write_orders · read_price_rules · write_discounts · read_locationsAgents apply trade discounts and route by fulfillment location.
Prompt · Your third flowI have two n8n workflows working: a weekly competitor review and an industry trend review, both scheduled, both using an AI agent with a web-search tool and a code node that builds an HTML email. Here is a description of a report my team would want every [week/day]: [describe it — what data, from where, who gets it]. Using the same pattern — schedule → configuration node → agent or lookups → code node for formatting → send — design the workflow, tell me which steps need AI and which should be plain code, and list what credentials I'd need. Don't build it yet; show me the node list first.
Part three · Rung three

The assistant that
never sends.

There's no file to import here, because the assistant is built around your inbox, your products, and your rep's voice. What I can give you is the design that made it safe enough to turn on — and the one rule that matters more than the rest of it combined.

The pattern

Drafts only. Always.

The inbox assistant reads an email that arrives for a rep, works out what kind of request it is, looks up whatever real data it needs, and writes a reply in the rep's voice — into the rep's own Drafts folder, with a note on top saying what it checked and what to verify. Then it stops. It cannot send. Only the rep can.

That single constraint is the difference between an assistant a sales team trusts and one they turn off in a week. Nothing reaches a customer without a person reading it first. The rep is always the sender, the voice, and the final approval. The assistant preps the shot; the rep takes it.

The flow, in five steps

1Email arrivesA trigger polls the rep's inbox (Microsoft Graph for Outlook; Gmail has its own node). A list of senders to skip — internal, newsletters, automated — keeps it from drafting replies to itself.
2ClassifyA model reads the email and returns one label: order status, quote request, catalog request, trade application, policy question, or "not for me." JSON out, nothing else.
3Look up real dataEach label has its own branch that fetches facts — order and tracking from the ERP hook (Part 4), stock and price for a SKU, the trade application on file. If the lookup finds nothing, the branch says so.
4Draft in the rep's voiceA model writes the reply using only the looked-up facts and two or three of the rep's own past emails as a voice sample. The draft opens with an assistant note: what was checked, what to verify, delete before sending.
5Save as draft. Tag. Stop.The draft lands in the rep's Drafts folder. The original email gets a category tag — Assistant-Drafted, Skipped, or Urgent — so you can measure coverage later. The workflow ends.

The guardrails, in order of importance

Drafts onlyThe workflow has no send node. Not a setting — a structural absence.
No invented numbersPrices and stock come from a lookup or don't appear. Asked to quote a product that didn't exist, ours refused and flagged it for a human. That was the day I trusted it.
Inbox onlyIt works the mail that comes in. Outbound, calls, and outreach stay with the rep.
Claims stay accurateSame rules as the pages: origin only where true, net-priced lines never discounted. The rules list from Part 1 is the rules list here.
Everything is taggedSo coverage is measured, not assumed. After two weeks you know what share of the inbox it handles and which drafts go out unchanged.
One config nodeRep name, mailbox, voice samples, territory, skip list — all in one place. Mirroring to the next rep is a copy and ten minutes.
How we rolled it out: the builder's own inbox first — four integration bugs found there instead of in a rep's. Then one rep for two weeks, measuring. Then the rest of the team, same engine, each with their own name and voice. The reps never learned a tool. They live in email exactly as they did before; the drafts are just there.
Prompt · Design the assistantI want an n8n workflow that reads a sales rep's inbox, classifies each incoming email into one of these types [list them], looks up real data for each type from [Shopify / my ERP / a sheet], and writes a reply draft in the rep's voice into their Drafts folder — never sending. Design it: the trigger, the classifier (JSON output, one label), one lookup branch per type, the drafting prompt (facts only, opens with a note of what was checked), and the save-as-draft step. Put every per-rep setting in one configuration node. Show me the node list and the classifier prompt first.
Part four · Rung three, and everything above it

Wire the agents to the
system of record.

Shopify is where our customers order. NetSuite is where the truth is — stock, lead times, whether a box actually left the building. For a year our agents could see the first and not the second, so every "is it in stock" question went to a human. This part is how we fixed that in one afternoon, for nothing, and why it's the most reusable thing we've built.

The ERP hook

One webhook, every agent.

The mistake I almost made was bolting a NetSuite lookup into one agent's workflow. Six agents, six copies, six things to drift. Instead it's one n8n workflow any agent can call with a small JSON request and get back a sentence to say plus the facts behind it. The agent reads the answer. The workflow decides what the answer is.

4 hrs
Idea to an agent answering live
$0
No new NetSuite seat
5
Actions: stock, status, tracking, send tracking, invoice

The NetSuite side, in order

1
Enable Features → SuiteCloud: REST Web Services, Token-Based Authentication. Analytics: SuiteAnalytics Workbook. Needs Administrator — borrow it for an afternoon.
2
A read-only role. View on sales orders, fulfillments, invoices, customers, items, inventory. Plus REST Web Services and Log in using Access Tokens (Full) and SuiteAnalytics Workbook (Edit). Nothing else.
3
Don't create a service user. NetSuite counts it as a full seat — ours would have been $10,000. Add the read-only role to the most permanent person in the building (our CFO) and issue the token under their user. The token only carries the role's permissions, not theirs.
4
Integration record (Setup → Integration → Manage Integrations): Token-Based Authentication on, everything else off. Copy the Consumer Key and Secret the moment they appear.
5
Access Token (Setup → Users/Roles → Access Tokens): that user, that role, that integration. Copy Token ID and Secret. Then the Account ID from Company Information — the one under Time Zone, not the EIN.

Five values into n8n's Variables, and NetSuite is done. You never log into it again for this.

What it says now

"How many of the TM100 do you have?""We have 29 of the TM100-1248RO in stock and ready to ship. Want me to put a quote together?"
"I need forty of them.""We have 29 that can ship right away. The other 11 would be about six days behind. Want me to quote it as one order, or split it?"
"Where's my order 22411? ZIP is 77005.""Order 22411 shipped September 3rd via FedEx Ground. Want me to email the tracking to the address on your order?"

The rules inside the workflow, not the prompt

  • Identity is enforced in n8n. Order lookups fail closed unless the caller's email or ship-to ZIP matches the record. Wrong ZIP: "For your security I'll have a team member help you."
  • Tracking only goes to the email on the order. Never to an address the caller offers. We tried to trick her within the hour; she refused.
  • Counts are computed, never recited. The workflow turns on-hand, available, backordered and lead time into one sentence. Made-to-order items get a lead time, not "out of stock."
  • When it doesn't know, it says so. No tracking yet, no lead time, item not found, ERP down — each returns defer_to_human and a sentence to say while capturing a callback.
Why this rung is gold. The webhook that answers a customer at 8pm answers a rep at 10am who'd otherwise leave Shopify, log into NetSuite, and hunt. Point the inbox assistant at it and it drafts stock replies. Point the supervisor at it and management can ask "do we have ten of these" out loud. Build it once and every agent you add afterwards is born knowing the truth.
Things that will go wrong

Field notes from one afternoon.

Each of these cost five to twenty minutes. Together they're the difference between "it works" and "it works first try for the next person." Your ERP will have its own list; finding them is the same every time — the tool returns the raw error, you read it, you change one line.

What happenedWhyThe fix
"Connection cannot be established"I'd copied the EIN into the Account ID variable. They sit side by side on Company Information.Account ID is the number under Time Zone. Check the URL the workflow builds first.
NetSuite answered 200, workflow said errorNetSuite replies as vnd.oracle.resource+json; n8n leaves the body as a string.One line: parse the string if the object isn't there. Now in the flow.
Order 22411 not foundShopify orders arrive as SO407551 with the web number in PO# — as #22411, hash included.Match on tranid, SO+number, or PO# with or without the hash.
"Record trackingnumbermap was not found"The tracking table I expected isn't exposed to SuiteQL. Neither was my second guess.They roll up onto the sales order in linkedtrackingnumbers. No join needed.
Status came back as "G"SuiteQL returns sales-order status as a code.BUILTIN.DF(status) gives the label; strip its "Sales Order :" prefix.
Consumer key vanishedThe key shows once. I closed the tab.Reset Credentials on the Integration record — which also kills every token under it. Reissue the token.
Agent said "let me check" twiceThe prompt said it, then the platform's pre-tool speech said it again.One owner for the filler: tool pre-tool speech on force, line deleted from the prompt.
Agent kept using the old Shopify order toolTwo tools could answer "where's my order"; the model picked whichever fit the moment.One tool per question. Remove the other from the agent; don't just prefer it in the prompt.

The pattern, so you can do it for your ERP

Webhook → normalize → validate → build and sign the query → one HTTP call → interpret rows into a sentence and a data object → respond. Invalid requests never reach the ERP; they get a "what's the order number?" sentence instead. Every branch ends with the same fields — found, defer_to_human, needs_input, spoken, data — so every agent reads every answer the same way. Swap the query builder and the interpreter and it's a Dynamics, SAP B1, or Acumatica hook.

Test it before an agent touches it

Hit the webhook from a terminal with a real SKU, a made-to-order item, a bogus one, then an order with the right ZIP and the wrong ZIP. Read the raw JSON. Only when those five come back right do you add the tool to an agent — then run the same five through the agent's test panel and listen.

Test the send without sending. A RESEND_TEST_TO variable, while it exists, routes every tracking email to you. Deleting it is the go-live switch — put that on a checklist.

Flow C — ERP Lookup Tool (NetSuite)

File: Starter_ERP_Lookup_Tool.json · 11 nodes · Runs on demand (webhook) · Uses AI: no — the agent that calls it is the AI
Download the flowAgent tool JSON

Any agent POSTs {"action":"item_availability","sku":"ABC-123","qty_needed":10} and gets a sentence to say and the facts behind it. Also order_status, tracking, send_tracking (emails tracking to the address on the order) and invoice_balance. Written for NetSuite's SuiteQL; the two nodes that know that are marked.

What to set · n8n → Overview → Variables
NS_ACCOUNT_IDCompany Information → the number under Time Zone, not the EIN.
NS_CONSUMER_KEY · NS_CONSUMER_SECRETFrom the Integration record, shown once on save.
NS_TOKEN_ID · NS_TOKEN_SECRETFrom the Access Token, shown once. Issued under a long-tenured user with the read-only role.
RESEND_API_KEY · RESEND_FROM_ORDERS · RESEND_REPLY_TOFor send_tracking. Sender on your verified domain; reply-to a mailbox a person reads.
BRAND_NAME · BRAND_PHONE · BRAND_URLUsed in the tracking email. Defaults are obvious placeholders.
RESEND_TEST_TOOptional. While it exists, every tracking email goes here. Delete to go live.

What each node does

1WebhookPOST /erp-lookup. Flat body or an ElevenLabs tool call.
2Normalize InputCleans fields, strips "order"/"#" from order numbers, validates what each action needs.
3Valid Request?Bad requests skip the ERP entirely and get a "what's the order number?" sentence from Bad Request Reply.
4Build Query + Sign · NetSuite-specificOne SuiteQL query per action, and the OAuth 1.0 signature (HMAC-SHA256) NetSuite wants. Tunables at the top.
5ERP SuiteQLThe single HTTP call. Never errors — the next node reads the status.
6Interpret Result · NetSuite-specificRows → sentence + data. Low-stock threshold, made-to-order types, identity check and the email template live here.
7–9Needs Email? · Resend · Confirm Sendsend_tracking only. Sends to the address on the order, then confirms or defers.
10RespondAlways the same shape: found, defer_to_human, needs_input, reason, spoken, data.

Add it to an agent

ElevenLabs → agent → Tools → Webhook. Name it erp_lookup, POST to the production URL, parameters action, sku, qty_needed, order_number, email, zip, plus constants channel = voice and agent = its name. Give each agent only the actions it should have. In the prompt: read spoken word for word; on defer_to_human, capture a callback; on needs_input, ask and call again. Our tool JSON is in the download.

When it isn't working

netsuite_error_401Token revoked, credentials reset, or the user lost the role.
netsuite_error_403The role is missing a permission. Compare to step 2.
netsuite_error_400A table or column NetSuite doesn't expose to you. error_detail names it.
"Connection cannot be established"Account ID wrong; check the URL in Build Query + Sign.
Part five · Rung four

Before the
agents.

This part isn't how to build a voice agent. That's a longer document and a longer year. This is what has to already be true before you try — because every one of these is something we had to go back and fix after the agents were live.

The checklist that would have saved us months

What has to be true first.

Your data can answer the questions

An agent on the phone gets asked "does it come in walnut," "what's the lead time," "is it in stock," "what's my trade price." If the answers aren't in structured fields a lookup can reach, the agent guesses or stalls. Part 1 is not optional before Part 5, and Part 4 is what makes "is it in stock" answerable at all. Ours guessed on a discount for months because the page and the prompt disagreed.

Your rules are written down

Discounts by product line. Which lines are net. Where origin can be claimed. What the agent must never say "done" about until a tool succeeds. We keep one canonical table; every agent prompt and every page derives from it. When the table changed and one agent didn't, a customer heard the wrong number.

Reps are first, agents are the net

During business hours our reps get sixty seconds of first refusal on every call. The agents catch what the reps can't reach; after hours they carry it alone. Decide this before the first call, and tell the team. "Collaborate, don't replace" is a design constraint, not a slogan.

Every hand-off is warm

When an agent transfers to a person, the person hears the context — not a cold ring. When an agent transfers to another agent, the opener is a greeting, not "Sure thing" to a sentence nobody said. Test every transfer path yourself, on a real phone, before a customer does.

Names get spelled back

A speech-to-text slip turned "Ryse Construct" into "Arise Construction" on a $24K quote. Every agent now spells company names back phonetically. Proper nouns are a business risk; treat them like one.

Someone reads the calls

Six agents answering around the clock produce more conversation in a week than a manager can read in a month. We gave the reading job to a seventh agent: every Monday it reads every transcript and flags what a manager would flag — pricing ambiguity, failed transfers, outcomes logged as success that weren't. It recommends; a human decides. In its first week it cleared an agent of a complaint. Build supervision in before you need it.

What it costs, once you're here: about $1,500 a month for six agents, a supervisor, phone lines, models and automation — roughly $2.50 per conversation over our first twelve weeks. And months, honestly. The pages took afternoons. The flows took weekends. The agents took the better part of a year, most of it in the seams between systems. Start at the bottom of the ladder and let each rung pay for the next.

Where this goes next

Everything in this kit was built by one person directing an AI, at a 62-person company, with tools that cost less per month than a single agency invoice. If you build something from it, tell me — the address is on the handout. And if something in here is wrong or unclear, tell me that first.

White River Hardwoods · AI Team · Fayetteville, Arkansas · Share freely. The three workflow files, this kit, the handout, and the full build journal all live at whiteriver.com/pages/ai-talk.