> cat speaking-at-suiteworld-2026.md

Taking a segregation-of-duties engine to SuiteWorld

I’m speaking at SuiteWorld this year. October 27, Administrator track, a session called “AI Meets Access Management: Smarter Role Provisioning and Risk Detection in NetSuite,” alongside two product managers from Oracle NetSuite. They’ll cover what Oracle is shipping: NetSuite Expert, Prompt Studio, AI Agents. I’m the customer on the panel, which means my job is to show up with something that already runs.

SuiteWorld speaker card: James Martin, IT Manager, Every Man Jack. Allergic to slideware and roadmaps.

So here’s what I’m bringing.

The idea that makes the whole thing work

Every segregation-of-duties rule is a fraud typology written as a pair of permissions. “Maintain vendors” plus “pay vendors” is fictitious-vendor fraud. “Apply customer payments” plus “issue credit memos” is lapping. Write the rule that way, with exact NetSuite permission keys instead of squishy labels, and it stops being a compliance checkbox and becomes a query.

rules.ts
{
id: 'SOD-AR-02',
name: 'Apply customer payments and issue credit memos',
category: 'Accounts Receivable',
severity: 'critical',
rationale:
'Applying receipts and issuing credit memos in one role lets a person hide a diverted payment behind a write-off (classic lapping/write-off fraud).',
a: { label: 'Apply customer payments', permkeys: ['TRAN_CUSTPYMT', 'TRAN_DEPAPPL'], minLevel: 'C' },
b: { label: 'Issue credit memos', permkeys: ['TRAN_CUSTCRED'], minLevel: 'C' },
}

The first version matched on permission labels with substring searches. It found twice as many “conflicts” as the exact-key version, and a good chunk of them were nonsense: “Pay Bills” conflicting with itself, a payroll rule lighting up on payroll reports. Switching to exact permkeys was the first real correction Claude and I made after the initial build. The numbers got smaller and truer.

Prevent: the SuiteApp

The console is a SuiteApp hosted inside NetSuite as a pair of Suitelets. Two SuiteQL queries feed it: every role’s permissions, and every employee’s role assignments.

SuiteQL
SELECT rp.role AS role_id, r.name AS role_name, rp.permkey, rp.permlevel
FROM rolepermissions rp JOIN role r ON r.id = rp.role
WHERE rp.permlevel > 0;
SELECT e.id, e.entityid, e.isinactive, e.giveaccess, er.role, r.name AS role_name
FROM employeerolesforsearch er
JOIN employee e ON e.id = er.entity
JOIN role r ON r.id = er.role;

From those two result sets it builds a role-by-permission matrix, runs every rule against every role, then folds roles into people. That last step matters. A role can be clean and a person can still hold a conflict, because they have three roles. The console shows both views, plus the sensitive-permission list and the things nobody wants to talk about, like inactive employees who still have login access. Users open it under a purpose-built role whose only permission is reading the file cabinet; the data Suitelet does the querying. Read-only, on purpose.

Detect: the same rules, applied to behavior

A role holding a conflict is a risk. A person exercising it is an incident. So the second half runs the same rule ids against transactions. Here’s the detection that realizes SOD-AR-02: credit memos issued by someone who applied a payment to the same customer in the preceding thirty days.

SuiteQL (detection D5)
SELECT c.id AS credit_id, c.tranid, c.createdby, c.entity AS customer_id,
p.id AS payment_id, p.tranid AS payment_tranid
FROM transaction c
JOIN transaction p ON p.entity = c.entity AND p.type = 'CustPymt' AND p.createdby = c.createdby
AND p.createddate >= c.createddate - 30 AND p.createddate <= c.createddate
WHERE c.type = 'CustCred' AND c.createddate >= :from AND c.createddate < :to
ORDER BY c.id, p.id;

Five detections like that run every weekday morning against production over a read-only token. The findings are deterministic SQL. A Managed Agent gets involved only afterwards, to read the evidence and write the brief that lands in Slack. The LLM never decides what counts as a finding. It explains one.

The take I’m bringing to the room

The AI part of “AI meets access management” is the easy part. Claude wrote most of this code and the tests that keep it honest. The hard part is the boring, deterministic layer underneath: exact permission keys, a rule set you can defend to an auditor, and a hard line between “the query found it” and “the model summarized it.” Get that layer right and the AI on top is trustworthy. Skip it and you have a chatbot that’s confidently wrong about who can pay whom.

SuiteWorld session card: AI Meets Access Management, Administrator track, Tuesday October 27, 3:45 PM, Academy 411

Academy 411, 3:45 PM. I’ll be the one without a roadmap slide.

<- back to archive