Suite Tooth Consulting
All articles
Integration

My Customer Built His Own Field Service App. The Build Was the Easy Part.

6 min read
Reclaimed wood framing meeting a steel frame at a clean seam

A non-developer built a working field service app with Claude Code. Then he called us — because connecting it to NetSuite turned out to be a completely different, and more interesting, problem.

One of my customers got tired of the field service software he was paying for. So he built his own.

He is not a developer. He runs the company.

He used Claude Code and he did it himself. He now has a working application with customers, locations, jobs, parts, and time entries in it, running on a Postgres database. His team is going to operate out of it.

Then he called us. Not to fix the app. The app works.

He called because the app needed to talk to NetSuite. That turned out to be a completely different problem, and a more interesting one.

What he actually wanted

He was clear about the shape of it, which helped.

Get a project number at the start of a job. Do the operational work in his own app, where his people already are. Create the invoice at the end from his app's data.

He'd tried the other direction before, running the whole job as a chain of transactions inside NetSuite. He didn't want to go back to it. His crews don't want to bounce between two systems all day, and he doesn't want his field operations bent into a shape that suits an accounting flow.

That's not an unreasonable position. It's also not a position you can act on until you decide what belongs where, and that decision is the entire project.

The question that took an hour

Before our technical call, he and his controller did their homework. They researched how to key the two systems together, and the research kept pointing them at the NetSuite internal ID.

Reasonable answer. Incomplete one.

Every record in NetSuite has an internal ID. The system generates it and you cannot change it. Every record can also carry an external ID, which you set yourself, and which exists for exactly this purpose: holding the key from another system.

So it isn't one or the other. It's both, pointing in opposite directions. NetSuite's internal ID goes into a column in his database. His database's record ID goes into NetSuite's external ID field.

That's what makes the sync safe to run twice. Change a customer's name and the integration finds the record that's already there and updates it, instead of quietly creating a second one. Get this wrong and you don't find out on day one. You find out six months later when someone asks why a customer appears four times on an aging report.

It's not a hard concept. But you either know how NetSuite stores keys or you don't, and no amount of prompting gets you there if you've never had to clean up the duplicates afterward.

Worth saying plainly: the research wasn't wrong. It answered one half of a two-part question. That gap between a correct answer and a complete one is most of what my job is now.

Then it got more interesting

Once we started mapping his tables against his actual NetSuite account, the questions stopped being about code entirely.

How deep does the customer hierarchy go? He said two levels, parent and child. Twenty minutes into the call he worked out it was three: parent company, child customer, and multiple service addresses underneath. His instinct was to add another table. The better answer was a self-referencing key on the table he already had. We also talked about the general solution that handles arbitrary depth, and then didn't build it, because he doesn't have arbitrary depth. Building for a problem you don't have is its own kind of expensive.

What happens when a record disappears on one side? Our first design deleted the matching row in his database. He stopped that immediately, and he was right to. His database is operational. Deleting a job because something changed in NetSuite could take an unbilled invoice with it. We moved to a flag and an index instead, which is roughly what NetSuite already does with inactive records.

Which system owns what? NetSuite owns the customer. His app owns scheduling and the field work. Addresses live in NetSuite and flow outward. That sounds obvious written down. It took a full call to say out loud in a way that everyone agreed on, and everything downstream depends on it.

How does the invoice get back? An endpoint in NetSuite. A button in his app that calls it. NetSuite builds the invoice, then hands the invoice ID back so both systems know it exists. One action, one round trip, no copy and paste, no orphaned records on either side.

What permissions does the integration get? Read and write. Not admin. An integration should never be in a position to drop your database, and neither should the consultant. He kept the keys to his own schema and gave us exactly what the job needed.

And a quiet one. When we pulled his customer list to plan the initial load, it came back with far more records than he expected. Most were inactive. He had cleanup he didn't know about. Connecting two systems is a remarkably effective way to find out what's actually in your data.

None of that is programming. All of it is judgment about how a business's information should be shaped, and what it costs you later if it's shaped wrong.

The moment I knew this was different

Late in one of these calls, out of nowhere, he asked whether we could put a custom button on the project record in NetSuite to generate an invoice. Then he talked himself through the two ways to do it, decided a sales order made more sense than going straight to an invoice, and answered his own question.

Six months ago that's a question he pays someone to think about. Now it's something he reasons through in real time, because building his own software taught him how software thinks.

That's the change. It isn't that customers can suddenly write code. It's that building something rewires how you see your own operation. You start noticing where the friction is and asking whether it has to be there.

I'm not neutral about any of this

Suite Tooth runs on software we wrote for ourselves. Time tracking. Invoicing. A document-to-record automation that turns files into NetSuite records without anyone rekeying them.

Those aren't demos we built to have something to show. We use them every day to run the business, and we built them much the same way my customer built his.

So I don't find this threatening. The cost of building something has dropped hard and it isn't going back up. What hasn't dropped is the cost of getting the data model wrong, and that bill doesn't arrive up front. It arrives during a close, or during an audit, or the first time someone tries to reconcile two systems that disagree about who the customer is.

We're still mid-build on his integration as I write this. Customers and locations first, then parts, then invoices flowing back from his app into NetSuite. I'll write about how it goes, including the parts that don't go well.

So build it

If you've been thinking about building your own app, build it. The tools are good now, and you know your operation better than any vendor ever will.

Just be honest with yourself about where the difficulty actually lives. It isn't in the app. It's in the seam between your app and your system of record, and that seam is where your accounting sits. It doesn't forgive guesses.

If you'd rather not learn that part the expensive way, that's the work we do.