The problem this solves
Your general ledger says cash is $4.2M. Is it? A reconciliation proves the balance by tying it to something independent and explaining every difference. Under SOX 404 and equivalent regimes, account reconciliation is among the most heavily tested internal controls, because it is where misstatement is caught — and the auditor does not simply want the reconciliation. They want evidence that a qualified preparer performed it, that an independent reviewer approved it, that both happened on time, and that supporting documentation was attached and retained. The evidence is the control; the arithmetic is only part of it.
The manual version — 2,000 accounts × 12 periods = 24,000 Excel templates on a shared drive, email for submission and review, a tracking spreadsheet nobody trusts, three weeks of hunting for evidence at audit time — fails the same way at every client: no reliable status view until it is too late, reviewers rubber-stamping because everything arrives on day eight, no enforcement that preparer and reviewer are different people, and no way to see aging across the portfolio.
Audience
Controllers and close managers who own the reconciliation process. Preparers and reviewers who live in the worklist. Internal audit and SOX teams who test the control. EPM architects deciding whether ARCS is a module of FCCS (it is not — separately licensed, implemented separately, usually sequenced after) or a product in its own right.
A profile, instantiated for a period
| Profile property | What it drives | In this build |
|---|---|---|
| NLQ query: “Show the bank reconciliation for NYC Flagship” or “Suspense account at Chicago Mag Mile” — DeepSeek resolves entity, account type, year and month; the profile is looked up and its reconciliation for that period built | ||
| Account ID | The unique key — usually Entity-Account-CostCentre | S_101-A_11001 (entity × account) |
| Format | Which template the reconciliation uses (and therefore its method) | 8 formats: Bank, Sub-ledger, Intercompany, Fixed Assets, Prepaid, Accrual, Suspense, Variance |
| Frequency | Monthly, quarterly, annual | Monthly; quarterly for Low-risk Variance profiles |
| Risk rating | Scrutiny, review levels, documentation | High / Medium / Low, from account type, upgraded for material sub-ledgers at large entities |
| Preparer / reviewers | Who does the work and who approves, in sequence | Regional Close Teams prepare; named reviewers, plus the Group Controller as level 2 for High risk |
| Workflow dates | Start, due, close as offsets from period end | Due day +5 / +8 / +12 by risk tier |
516 profiles across 44 entities and 12 account types. Profiles are copied into a period when it opens, so a change to a profile is forward-looking by default — tell clients in training or you will field the same question every month.
One reconciliation, method-specific, with its evidence trail
- KPI row: GL (source system) balance, the method-specific counterpart (subsystem balance, total explained, or prior balance), the difference/unexplained/variance, and the workflow status with late and auto-reconciled flags
- Reconciliation panel: Balance Comparison shows GL vs subsystem and the reconciling items with ages; Account Analysis shows the supporting items summing to the balance with any unexplained residual; Variance Analysis shows the movement against threshold
- Aging chart: the reconciliation’s items by 30/60/90/180+ bucket — the signal that matters
- Workflow & evidence: profile properties, the status pipeline, the timestamped history (who, what, day, how long), attachment status, and the format’s questions
- The trap button: jumps to a profile whose entity code changed in the GL — loaded $0, nil difference, auto-passed
ARCS proves the balances; FCCS combines them
Assurance, then aggregation. This use case is the workflow and control engine at balance level — what most clients mean when they say they are implementing ARCS. Transaction Matching (UC11) is the second engine, at transaction level, and its unmatched balance feeds a reconciliation here. Aging & Audit Evidence (UC12) is the portfolio view built on top of every reconciliation this page can show one at a time. The GL balances themselves come from the same balance sheet the FCCS module (UC7–9) consolidates.
Recommended integration points
- Close-day triage: a preparer asks “what is open with me for Aug” and lands on the exact reconciliation, evidence and questions included
- Audit walkthrough: the auditor picks an account; the timestamped history and the attachment status are the evidence package
- Design workshops: use the method-per-account-type table and the trap scenarios to argue for risk tiering in front of the controller who wants to review everything
The numbers
The honest checklist
- ✓Your team reconciles by explaining a difference between two totals, and the pain is control and visibility — who did what, by when, with what evidence
- ✓You have hundreds to thousands of accounts and one process for all of them, and reviewers have stopped reading
- ✓Audit spends weeks assembling reconciliation evidence that a system should produce as a by-product of the work
- ✗The pain is volume — matching hundreds of thousands of line items against each other. That is Transaction Matching, and Reconciliation Compliance alone will disappoint
- ✗You cannot produce the definitive account list or the sub-ledger feeds — the profile spreadsheet is the critical path, and Balance Comparison without a second feed is a data problem no configuration solves
Try it now
The live demo builds any of 516 profiles’ reconciliations for any month of FY25 or FY26.
Things to try
- Type “Suspense account at Chicago Mag Mile” and read the item ages — a suspense account holding items older than 60 days is one of the most reliable indicators of a broken upstream process anywhere in finance
- Press “Show the account-key trap”: a $0 balance, a nil difference, and an auto-pass. Then open UC12 and find it in the completeness check
- Pick Retained earnings for August, then September — a quarterly Variance profile is not scheduled off quarter-end, and method and frequency are separate levers
Risk-based thinking is what separates a good implementation from a bad one
Not every account deserves equal scrutiny. A $2Bn goodwill balance and a $400 petty cash float should not follow the same process. Clients who apply one process to all 2,000 accounts drown, and their reviewers stop reading; clients who tier by risk concentrate genuine scrutiny where it matters. Risk is a property on the profile and it should drive four things together — frequency, method, review levels, and documentation. Setting a risk rating without letting it drive anything is the most common half-implementation: if High and Low profiles follow an identical process, the rating is decoration.
| High | Medium | Low | |
|---|---|---|---|
| Frequency | Monthly | Monthly / quarterly | Quarterly / annual |
| Method | Account Analysis or Balance Comparison | Either | Variance Analysis |
| Review levels | Two (manager + controller) | One | One, or auto |
| Documentation | Attachment mandatory | Attachment mandatory | Optional |
Assignment criteria — materiality, judgment, volume, error history, SOX-key designation — are agreed in design and applied mechanically to the profile list. Otherwise every account becomes High, because no controller wants to defend calling their own account low-risk.
Profile universe → method branch → workflow snapshot
- Each profile: Account ID, Format (⇒ method), risk, frequency, preparer Team, reviewer chain, due-day offset
- Risk from account type, upgraded to High for AR/AP/Inventory at entities with revenue ≥ $15M
- The same balance sheet FCCS consolidates — ARCS proves what FCCS combines
- Key-mismatch profiles load $0 while the true balance is retained for the completeness check
- 0–4 timing items with ages
- Difference fully explained
- 2–4 items composing the balance
- 12% carry an unexplained residual
- Threshold 10% or $100K
- Explanation only when exceeded
- Closes with a documented reason in the history
- Never applied to High or Medium risk — the scope limit that keeps it defensible
- Pending → Open with Preparer → Open with Reviewer (levels in sequence) → Closed, with the Rejected loop
- Every action carries who / what / day / seconds — the timestamps that expose rubber-stamping later
- Method-specific table, aging chart, workflow pipeline, evidence panel
- The account-key trap button
App UI — Component breakdown
| Component | Behaviour |
|---|---|
| Filters | Entity (44), account type (12, labelled with its method), year, month. Combinations outside the profile universe explain why rather than failing silently |
| KPI row | GL balance, the method’s counterpart, the difference / unexplained / variance, and status with late and auto flags |
| Reconciliation panel | Three renderings, one per method; suspense accounts with items over 60 days get an inline warning |
| Aging chart | This reconciliation’s items by 30/60/90/180+ bucket (BC/AA) or prior-vs-current (VA) |
| Workflow & evidence | Profile properties, status pipeline, timestamped history with review durations, attachment status, format questions |
| Trap button | Loads a key-mismatch profile and explains the silent $0 auto-pass and its two defences |
Template versus instance
Two objects carry nearly all Reconciliation Compliance configuration. A Format is the template: it defines the method, which attributes appear, the questions the preparer must answer, the checklist, the currency buckets displayed, and the rules about attachments and explanations. It is reused across hundreds of accounts — typically 8–15 for an entire implementation. If you find yourself building 200 formats, you have misunderstood the object. A Profile is the master record for one account (or a group), persisting all year and generating a reconciliation each period.
Profile (permanent) + Period (August 2026) → Reconciliation (the instance)
// genReconciliation(profileId, year, period) is a pure function of those three keys.
// Nothing on the page can change a reconciliation retroactively — exactly how ARCS
// snapshots behave when profiles are copied into a period.
The operational consequence is worth training explicitly: changes to a profile do not retroactively affect reconciliations already created. Change a preparer in September and August’s open reconciliation keeps the old assignment until profiles are re-deployed to the period. For bulk changes, re-run copy-to-period (tested in a non-production period first, not mid-close); for a one-off, reassign directly on the reconciliation.
Where the effort actually goes
Profile setup is the work. Not the formats, not the rules — building an accurate, complete, correctly-assigned list of 1,800 accounts with the right preparer, reviewer, risk rating and frequency on each. It is loaded from a spreadsheet the client must populate, and getting that spreadsheet right is the critical path of the entire project. This build generates its 516 profiles from the entity list and the account-type table for exactly the reason the framework recommends in production: build profiles from the GL extract, not from a hand-maintained list.
Choosing per account type is the principal design judgment
Is there an independent external source for this balance?
YES → Balance Comparison
NO ↓
Is the account high or medium risk / material?
YES → Account Analysis
NO → Variance Analysis
// chooseMethod(hasExternalSource, isMaterialOrHighRisk) — three lines, unit-tested
| Account | Method | Why |
|---|---|---|
| Cash at bank | Balance Comparison | The bank statement is independent |
| Accounts receivable / payable, inventory, fixed assets | Balance Comparison | The sub-ledger or register provides the counterpart |
| Intercompany | Balance Comparison | The counterparty entity balance |
| Prepaid expenses, accrued liabilities | Account Analysis | No external source; composition is what matters — and aging is the risk |
| Suspense / clearing | Account Analysis | Should be near zero — composition is the entire point |
| Retained earnings, small P&L accounts | Variance Analysis | Should only move for known reasons; proportionate scrutiny |
Account Analysis is the workhorse, typically covering more profiles than the other two combined. Balance Comparison has the prerequisite people forget: a second data feed. If the client cannot deliver the sub-ledger balance on a schedule, the method cannot work — and that is a data problem, not a configuration choice. Variance Analysis is what makes a risk-based approach practical at scale: 600 low-risk accounts sit on it and only the ones that moved get examined.
Two traps worth knowing
“It never changes, so put it on Variance Analysis.” The reasoning is backwards. A stable $40M goodwill balance shows nil variance, auto-passes every month, and receives no examination for years — and when it does move it is impairment, acquisition or disposal, exactly when full documentation is wanted. Materiality drives method; volatility does not. The legitimate version of the request is to reduce frequency to quarterly while keeping Account Analysis and a senior reviewer. Suspense and clearing accounts should be zero or close to it: Account Analysis with a tight threshold, and watch the aging — the demo plants aged suspense items at four entities for this reason.
What users experience daily, and what auditors test hardest
Pending → Open with Preparer → Open with Reviewer → Closed
↑ │
└────── Rejected ───────┘ (comment required)
Reconciliations become visible at the start date, so preparers never work on stale data; late status is computed automatically against the due date. Review levels run in sequence — one reviewer for Low and Medium, manager plus Group Controller for High — and a rejection at any level returns the reconciliation to the preparer. Resist configuring three levels everywhere: every additional level adds close days and reduces the attention each reviewer actually pays. Teams assign a role to a group so that one person’s absence does not block the close; this build uses regional Close Teams for every preparer role.
Segregation of duties
ARCS prevents the same user being preparer and reviewer on the same reconciliation, and so does this build (a unit test asserts it across all 516 profiles). What neither knows is the organisation chart: the tool cannot detect that the reviewer reports to the preparer, which is a genuine SOX weakness. That assessment is a design-time responsibility, and raising it unprompted marks you as someone who has done this before. When a controller asks to review everything, do the arithmetic for them: 1,800 reconciliations at three minutes each is 90 hours in a five-day window. They will approve in bulk without opening anything, converting a real control into a documented fiction that ARCS timestamps permanently — which is exactly what UC12’s reviewer table shows.
Auto-reconciliation, made defensible
Rules close a reconciliation automatically when defined conditions are met: zero balance, unchanged from prior period, no activity, source and subsystem agree exactly, difference within threshold. On a real portfolio these clear 20–40% with no human touch. Three things make that defensible to an auditor, and this build does all three: documented criteria (each auto-closure writes its reason into the history), scope limits (Low risk only — auto-closing a material account is indefensible however stable it is), and sample testing (the client periodically reviews a sample manually and retains the evidence). The threshold trap — “auto-reconcile anything under $10,000” — gets its own treatment in UC12.
Six fields: entity, account type, year, month
Reconciliation’s NLQ schema is {intent, entity, accountType, year, period, confidence}. Two things differ from every earlier use case: ARCS has no Plan/Actual scenario (a reconciliation is of actuals by definition), and the period is a month, not a quarter — the reconciliation calendar is maintained separately from FCCS periods. The account type is a 12-value enum with aliases (“bank rec” → CASH, “debtors” → AR, “clearing” → SUSPENSE), and the same ENTITY_ALIASES table the other entity-bearing use cases share resolves the entity. The few-shot examples cover the preparer vocabulary rather than the accountant’s: “bank reconciliation for…”, “prepaid analysis…”, “suspense account at…”.
Cost model — Cost per query
| Component | Detail | Cost |
|---|---|---|
| LLM input tokens | ~780 tokens (schema block + few-shot examples + query) | $0.000109 |
| LLM output tokens | ~45 tokens | $0.0000126 |
| Profile lookup + reconciliation build | Client-side, deterministic, once the query resolves | $0 |
| Total per query | ~$0.0001 | |
| With 2× safety margin | ~$0.0002 |
Slightly larger than the FCCS prompts because of the 12-value account-type enum with its aliases; still under a tenth of a cent with margin. The reconciliation itself — balance derivation, items, status, history — is deterministic client-side arithmetic.
Key files
| File | Role |
|---|---|
epm-nlq-src/assets/epm-arcs-data.js | ARCS_FORMATS, ARCS_ACCOUNT_TYPES, ARCS_RISK_POLICY, genProfiles(), chooseMethod(), genReconciliation() — the profile universe, the method branch, auto-reconcile rules, and the timestamped history |
epm-nlq-src/pages/reconciliation.html | UI: NLQ bar, entity/account/year/period filters, method-specific reconciliation table, aging chart, workflow pipeline, history, the account-key trap button |
epm-nlq-src/assets/epm-fccs-data.js | genConsolidationBS() — GL balances come from the same balance sheet FCCS consolidates, so ARCS proves the balances FCCS combines |
epm-nlq-src/assets/epm-data.js | Shared ENTITIES, PERIODS, ENTITY_SEEDS, SEASON |
functions/api/nlq-query.js | Shared NLQ endpoint; this use case uses useCase: "reconciliation" |
Why ARCS is a separate data file from FCCS
They answer different questions and are separately licensed products. ARCS proves each balance is real; FCCS combines them into group results — assurance, then aggregation. Keeping epm-arcs-data.js separate mirrors that: it consumes the FCCS balance sheet for its GL balances but adds its own objects (profiles, formats, reconciliations, match types, rules) and never writes back. The deterministic FNV-1a hash (arcsRand) means every reconciliation and every bank row is reproducible on any machine, which is what makes the trap scenarios teachable.
Tech stack — Every tool in this build
| Layer | Tool | Why |
|---|---|---|
| Data layer | epm-arcs-data.js (vanilla JS) | Deterministic profile × period reconciliation — the LLM only resolves entity, account type, and period |
| LLM | DeepSeek V3 | Shared NLQ endpoint; six-field schema with a 12-value account-type enum and month periods |
| Charts | Chart.js 4.x | Reconciling items by age bucket (BC/AA) or prior-vs-current (VA); shared library with the other kits |
| Edge hosting | Cloudflare Pages | Static file, no server compute needed for this use case |
| Build | Eleventy v3.1.5 | Copies epm-nlq-src/pages/ and epm-nlq-src/assets/ to _site/ verbatim |
Known attack surfaces
| Threat | Mitigation in this build |
|---|---|
| Zero balance auto-passes because the account key no longer matches the GL extract | keyMismatch is modelled explicitly (3 entities × 4 account types), shown as a trap on the page, and caught by the completeness check in UC12 — the mitigation is a control, not a hope |
| Preparer and reviewer are the same user | Enforced in genProfiles(): reviewers are named users, preparers are Teams, and a unit test asserts no profile lists its preparer as a reviewer. What the tool cannot see — the reviewer reporting to the preparer — is documented as a design-time responsibility |
| Reconciliation marked complete with no evidence attached | attachmentRequired comes from the Format; closed reconciliations missing a required attachment are counted (missingEvidence) and shown in red on both UC10 and UC12 |
| A material account placed on Variance Analysis because it is stable | Method is a property of the Format, and the Format is chosen by account type through chooseMethod() — materiality drives method; volatility drives frequency |
Guardrails — What prevents bad control math
- Reconciliation = profile × period, by construction:
genReconciliation()is a pure function of profile id, year, and period; nothing on the page can mutate a reconciliation retroactively, which is exactly how ARCS snapshots behave - Balance Comparison ties exactly: the subsystem balance is built as GL + reconciling items, so subsystem − GL always equals the sum of items — unit-tested to the dollar
- Account Analysis blocks submission on an unexplained residual: a non-zero
unexplainedforces the status to Open with Preparer — the preventive control auditors prefer to a detective one - Auto-reconciliation is scoped to Low risk only and every auto-closure carries its documented reason in the history — the three things that make it defensible
Who is asking, and what are they allowed to see?
The demo answers neither question — it has a cookie gate and no notion of a user. In production these are the two questions everything else rests on, and they have different answers: authentication is who you are, authorization is what you may see. Corporate SSO settles the first. Only Oracle EPM Cloud can settle the second, and the single most important rule in this section is that this application must never become the place where that decision is made.
9.1 · The identity chain, end to end
- MFA and Conditional Access are enforced here — device compliance, location, risk signals
- Returns an ID token (who the user is) and an access token (what they may call)
- Group membership arrives as a claim; the application never handles a password
- Verify signature, issuer, audience and expiry against the IdP’s published keys
- Read the group claims — there is no local user table and no local role table
- Short-lived access token with refresh-token rotation; session timeout set to the data classification
- The API is called as the user
- EPM enforces its own security natively
- Audit trail names the real user
- Preferred where the API supports it
- The application becomes the enforcement point
- Entitlements fetched separately, applied in one audited place
- Simpler and cacheable — and a filtering bug is a data breach
- Roles: Service Administrator, Power User, User, Viewer — assigned to groups, never to individuals
- Data level: ARCS profile assignment and organizational-unit security — a preparer sees their own queue, not the portfolio
- Group → role mapping lives in the platform, not in this application
- The user sees exactly what they would see logging into the source system directly — no more
- Every query logged against the real end user, never a shared account
9.2 · Federating the corporate identity provider
Oracle EPM Cloud does not replace your directory — it trusts it. The EPM Cloud identity domain is federated with the corporate IdP so authentication happens where it already happens, under policies security has already written.
| Identity provider | Protocol | Notes |
|---|---|---|
| Microsoft Entra ID (formerly Azure AD) | SAML 2.0 or OIDC | The common case. Conditional Access, MFA and device compliance are enforced at Entra and inherited automatically. On-premises Active Directory federates through Entra Connect rather than being integrated directly. |
| Okta | SAML 2.0 or OIDC | Same pattern; Okta groups drive EPM roles through SCIM provisioning. |
| OCI IAM (identity domains) | Native | Already present with Oracle EPM Cloud. Can be the primary IdP for a smaller estate, or a federated spoke of Entra/Okta for a larger one. |
| AD FS | SAML 2.0 | Still seen where the estate is not yet cloud-first. Works, but you inherit the on-premises availability of the token service — if AD FS is down, nobody logs in. |
For the browser application itself, use OIDC Authorization Code flow with PKCE. Not the implicit flow, which is deprecated and leaks tokens through the URL, and never a resource-owner password grant — a finance tool should not be capable of handling a password at all.
9.3 · From group membership to EPM roles
Roles are granted to groups, never to individuals, and the groups come from the directory. That one discipline is what makes joiner/mover/leaver work without anyone having to remember this application exists.
Entra ID group → EPM role / entitlement
──────────────────────────────────────────────────────────────────
FIN-EPM-Analysts → Planning User
FIN-EPM-Controllers-EMEA → Power User + EMEA data scope
FIN-EPM-Admins → Service Administrator
──────────────────────────────────────────────────────────────────
Provisioned by SCIM. Remove the user from the group and the
entitlement disappears on the next sync — including here.
For this use case the relevant native entitlement is: ARCS User, with preparer and reviewer assignment flowing from ARCS Teams rather than from this application.
9.4 · The architectural decision: who enforces?
This is the choice that determines whether the deployment is defensible. Both patterns appear in the diagram above; the difference is where the security boundary actually sits.
| Pattern A — identity propagation | Pattern B — service account + filtering | |
|---|---|---|
| How | The user’s token is exchanged (OAuth 2.0 on-behalf-of) for one scoped to the EPM API; calls are made as the user | A single read-only integration account calls the API; the application filters the results |
| Enforcement point | Oracle EPM Cloud | This application |
| Audit trail shows | The real end user | The service account — you must log the real user separately |
| Failure mode | Token plumbing is more complex; per-user rate limits apply | A filtering bug is a data breach, and the entitlement copy drifts from reality |
| Verdict | Prefer this wherever the API supports user-token authentication | Acceptable with discipline: narrowest possible service account, filtering centralised in one tested place, real user in every log line |
The shortcut to refuse. Pattern B built with a Service Administrator account and no filtering at all is the most common way this gets delivered, because it works perfectly in UAT — testers are usually over-entitled, so nobody notices that everyone can see everything. It fails at the first access review, and by then it is in production with real users depending on it.
9.5 · Data-level security is the part that matters
Role membership decides whether a user can open the application. It does not decide which rows they get back, and confusing the two is the most expensive mistake available here.
- For this use case: ARCS profile assignment and organizational-unit security — a preparer sees their own queue, not the portfolio.
- Apply it before aggregation, not after. Filtering a total that has already been computed across entities the user cannot see still leaks the total.
- The NLQ layer needs its own check. Layer 4 already validates that the resolved point of view uses approved members; production adds a second test — that the resolved POV sits inside this user’s scope — and it runs before the data call, not after. A natural-language interface is very good at asking for things politely; the authorization check must not care how the question was phrased.
- Fail closed. If entitlements cannot be resolved, return nothing and say so. An empty result is a support ticket; a permissive default is an incident.
9.6 · Provisioning, sessions and the leaver problem
- SCIM provisioning from Entra or Okta into the EPM Cloud identity domain (OCI IAM, formerly IDCS), covering joiner, mover and leaver. The mover is the case people forget — somebody changing region should lose the old scope, not accumulate both.
- No local user store. If this application keeps its own copy of who may do what, a leaver keeps access until somebody remembers to update it. Nobody ever does.
- Short-lived access tokens with refresh-token rotation; align session timeout with the data classification rather than with convenience.
- MFA and Conditional Access at the IdP — not reimplemented here. Device compliance and location policy come free with federation.
- Quarterly recertification of both the groups that grant access and the service account’s own entitlements, evidenced and signed.
- Break-glass access is a named, monitored, time-boxed account — never a shared credential in a password manager.
9.7 · What this means for Reconciliation Compliance
| Concern | Answer for this use case |
|---|---|
| Native entitlement required | ARCS User, with preparer and reviewer assignment flowing from ARCS Teams rather than from this application |
| Data-level control | ARCS profile assignment and organizational-unit security — a preparer sees their own queue, not the portfolio |
| Use-case-specific sensitivity | Segregation of duties is an authorization control here, not merely workflow. ARCS enforces that preparer and reviewer differ; it cannot see the org chart, so it will not detect a reviewer who reports to the preparer. That assessment is a design-time responsibility and belongs in the access review, not in the tool. |
9.8 · Security configuration checklist
- ✓Oracle EPM Cloud federated with the corporate IdP over SAML 2.0 or OIDC; the cookie gate removed entirely
- ✓Browser app uses OIDC Authorization Code + PKCE — no implicit flow, no password grant
- ✓MFA and Conditional Access enforced at the IdP, not reimplemented in the application
- ✓Roles granted to directory groups, never to individuals; SCIM covers joiner, mover and leaver
- ✓Enforcement pattern chosen deliberately — Pattern A where the API supports it, or Pattern B with filtering centralised and tested
- ✓Data-level security applied before aggregation, and the resolved POV checked against the user’s scope before the data call
- ✓No local user table and no local role table anywhere in the application
- ✓Every query logged against the real end user, even when a service account makes the call
- ✓Authorization failures fail closed and are logged as security events rather than swallowed
- ✓Quarterly recertification of access groups and of the service account’s own entitlements
From demo to a governed enterprise deployment
Everything above runs on synthetic data, a public LLM API key, a cookie gate, and no audit trail — deliberately, so the mechanics are inspectable. Taking Reconciliation Compliance to production is not a rewrite; the 4-layer pipeline and the data-layer contract survive intact. It is a controlled-change program across six workstreams: architecture, LLM platform, security, SOX/audit, environment promotion, and operations. This section is the checklist we run with clients.
10.1 · Production reference architecture
- Terminates SSO, validates the session, attaches the user’s EPM groups to the request
- Rate limits per user, blocks anonymous access, scrubs PII patterns before anything reaches the orchestrator
- L2 grounding reads dimension metadata from EPM on a schedule — not a hardcoded schema
- Only the schema + user query go to the model; financial values never leave the data layer
- L4 rejects anything outside the approved member lists and falls back to the deterministic parser
- Data stays inside the OCI boundary
- Natural fit when EPM is already in OCI
- Use the hyperscaler the org already governs
- Enterprise DPA, no training on prompts
- For regulated or sovereign data
- Highest control, highest run cost
- Oracle ARCS Reconciliation Compliance — profiles, reconciliations, statuses, and the timestamped workflow history via the ARCS REST API; balances and sub-ledger totals loaded by Data Integration
- Least-privilege service account (read-only role, one app, one pod) with the token in a vault and rotated
- Results filtered to the requesting user’s EPM security before rendering
- Every query logged: user, timestamp, raw query, parsed intent JSON, model + prompt version, POV returned, latency, cost
- Exported to the SIEM; retained per the SOX evidence schedule
- Dashboards for fallback rate, eval pass rate, guardrail hits, p95 latency, spend
- The parsed JSON is shown to the user as the explanation (“AI: entity · year · scenario — 93% confident”) — the same line the demo prints today
- Every number on screen traces to an EPM cell intersection an auditor can reproduce
10.2 · Choosing the LLM platform
The demo’s DeepSeek call is a placeholder for a single adapter, callLLM(system, user), behind Layer 3. Swapping the provider changes one function and zero business logic. Pick the platform the organisation already governs — the security and procurement review is the long pole, not the integration.
| Option | Choose when | Data posture |
|---|---|---|
| Oracle OCI Generative AI (Cohere Command, Llama) | EPM Cloud already lives in OCI; you want one cloud boundary and one contract | Prompts stay in the OCI tenancy; no training on customer data; dedicated AI clusters available for isolation |
| Azure OpenAI Service | Microsoft-first finance estate (Entra ID, Purview, Sentinel already in place) | Private endpoint, regional deployment, zero-retention by default under the enterprise agreement |
| AWS Bedrock (Claude, Titan) / Google Vertex AI (Gemini) | The org’s landing zone is AWS or GCP; VPC endpoints and IAM already audited | VPC/PSC private access, no data used for training, CloudTrail/Cloud Audit Logs integration |
| Direct enterprise API (Anthropic, OpenAI) | Fastest model access; acceptable when a zero-data-retention agreement and DPA are signed | ZDR endpoint, SSO-managed keys, SOC 2 report on file |
| Self-hosted open weights (Llama, Mistral, Qwen via vLLM) | Sovereign or air-gapped requirements; regulated data classification forbids any external inference | Full control; you own patching, eval, and capacity — budget for an MLOps owner |
Put a model gateway in front of whichever you choose (Azure API Management, OCI API Gateway, Kong AI Gateway, LiteLLM, or Portkey): it owns key custody, per-team spend caps, routing and fallback between models, prompt/response logging, and lets you retire a deprecated model without touching the application.
10.3 · Security controls
| Control | Implementation |
|---|---|
| Identity & access | Covered in full in section 09 — corporate SSO, group-to-role mapping, and the decision about who enforces data-level security. Listed here because it is a production gate, not because it is optional. |
| Service account | One read-only EPM service account per application per pod, least-privilege role, no interactive login, credential in a vault (OCI Vault, Azure Key Vault, HashiCorp Vault), rotated on a schedule and on staff change. |
| Secrets & config | No secrets in code or build artifacts; environment-specific config injected at deploy; .dev.vars-style files never leave a developer machine. |
| Network | Private endpoints to the LLM provider and to EPM where the platform supports them; egress allow-list so the orchestrator can reach exactly two hosts; TLS 1.2+ everywhere. |
| Prompt-injection & input guardrails | Layer 1 (already in the demo) blocks instruction-override patterns, enforces length and scope; extend with a classifier on the gateway and log every rejection. |
| Output guardrails | Layer 4 (already in the demo) validates every returned member against the approved lists and strips unexpected keys; production adds a policy check that the resolved POV is inside the user’s security scope before the data call. |
| Data minimisation | Prompts contain metadata and the user’s query only. No cell values, no employee names, no free-text comments from EPM. Logged prompts are classified and retained accordingly. |
| Encryption | In transit (TLS) and at rest (provider-managed KMS); audit logs on immutable storage with customer-managed keys where policy requires. |
10.4 · SOX, audit, and model-risk controls
A read-only NLQ layer does not change a financial-reporting control, but it is an interface to a SOX-relevant system and lands squarely in ITGC scope. Treat prompts, schemas, and eval sets as code — that single decision satisfies most of what an auditor will ask for.
| Requirement | How it is satisfied |
|---|---|
| Complete, immutable audit trail | Append-only log of user, timestamp, raw query, parsed JSON, model and prompt version hash, POV returned, and row count — WORM storage, retained for the evidence period (typically 7 years), exported to the SIEM. |
| Change management | Prompt templates, few-shot examples, approved-member schema, and code are version-controlled; every change follows ticket → peer review → test evidence → CAB approval → deploy. A prompt edit is a code change. |
| Segregation of duties | Developers cannot deploy to production; the service-account owner is not a developer; production secrets are held by platform operations. |
| Access recertification | Quarterly review of who can use the tool and of the service account’s EPM roles, evidenced and signed. |
| Testing evidence | A golden-query regression suite (the few-shot examples plus a larger labelled set) runs in CI before every release; pass rate and diffs are archived as release evidence. |
| Model risk management | An inventory entry (intended use, limitations, owner, validation date) in the model-risk register — the SR 11-7 pattern for financial services; periodic re-validation when the model or prompt changes. |
| Reproducibility & lineage | Every displayed number traces to an EPM POV and a consolidation/calculation timestamp; an auditor can re-query the same intersection in EPM and match it. |
| Explainability | The parsed intent JSON is the explanation and is shown to the user on every response — no hidden reasoning between the query and the data call. |
10.5 · Dev → Test → Prod promotion
| Environment | EPM target | Data | Gate to leave |
|---|---|---|---|
| Dev | EPM Test pod (developer slice) | Synthetic or masked | Unit tests on the data layer; lint; eval suite ≥ threshold against the Test LLM deployment |
| Test / UAT | EPM Test pod (full refresh) | Masked copy of production | Business UAT sign-off on the golden queries; security scan; performance run (p95 latency, fallback rate) |
| Prod | EPM Production pod | Live | Change ticket approved; deploy in window; smoke test; hypercare with rollback ready |
- Promoted artifacts: application build, prompt templates (versioned), approved-member schema snapshot, eval set, infrastructure config (IaC) — all from the same Git tag.
- Pipeline: branch → PR review → CI (tests + evals) → deploy to Test → UAT sign-off → CAB → deploy to Prod → smoke test. Hosting can stay on Cloudflare Pages/Workers or move to OCI Functions + API Gateway or the org’s standard platform — the code does not care.
- Configuration: per-environment secrets and endpoints injected at deploy; the same build runs in every environment.
- Metadata sync: a scheduled job refreshes dimension metadata into Layer 2 grounding with change detection, so a new entity or account appears in the approved lists without a code release.
- Rollback: previous build and previous prompt version retained; rollback is a redeploy, and because prompts are versioned it also reverts a prompt regression.
10.6 · Operating it
- SLOs: p95 latency, availability of the read path (the deterministic fallback keeps it alive when the LLM is down — already built), fallback rate as a quality signal, eval pass rate per release.
- Cost governance: per-user and per-team token budgets at the gateway; alert on anomalies; the unit-cost model earlier in this kit is the baseline.
- Model lifecycle: providers retire models on a schedule — re-run the eval suite on the successor before switching, and record the switch as a change.
- Incident runbook: LLM outage → fallback parser; EPM API outage → cached metadata with a stale banner; guardrail spike → review logs for injection attempts.
10.7 · What changes for Reconciliation Compliance
| Concern | Production answer |
|---|---|
| System of record | Oracle ARCS Reconciliation Compliance — profiles, reconciliations, statuses, and the timestamped workflow history via the ARCS REST API; balances and sub-ledger totals loaded by Data Integration |
| Read/write posture | Read-only. Preparation, review, rejection, and sign-off happen in ARCS; this layer never changes a status or an item. |
| Use-case-specific control | The audit trail must remain ARCS-native: never cache or restate workflow history in this layer, and always surface the period lock status so a user can tell final evidence from an open period. |
10.8 · Production readiness checklist
- ✓LLM platform selected from the governed list, DPA / zero-retention terms on file, gateway in front of it
- ✓SSO integrated; authorisation derived from EPM security groups; cookie gate removed
- ✓Read-only EPM service account per pod, credential in a vault, rotation scheduled
- ✓Prompts, schema, and eval set version-controlled and under change management
- ✓Append-only audit log wired to the SIEM with the agreed retention
- ✓Golden-query eval suite passing in CI; results archived as release evidence
- ✓Dev / Test / Prod pipeline with gates, IaC, and a rehearsed rollback
- ✓Model-risk register entry and owner named; first re-validation date set
- ✓Metadata refresh job scheduled with change detection
- ✓SLOs, cost caps, and the incident runbook agreed with platform operations