Inforgl ↔ OneStream over REST, with a shared-script library
Quick Facts
- Industry: Industrial machinery / logistics platform
- Role: Integration Lead — OneStream platform side, Inforgl REST contract owner
- Timeline: ~5 months, integration build then refactor-and-standardise pass
- Team: Solo build with two finance analysts validating each direction of the flow and the rule changes
- Impact: Manual GL-to-consolidation hop retired in favour of a live bidirectional REST bridge; per-workflow rule scripting replaced by a shared library that the rest of the team adopted as the default pattern.
Overview
The client ran their general ledger in Inforgl and their consolidation, planning, and management reporting in OneStream. The two platforms had no live link — every period close involved an analyst exporting from Inforgl, reshaping the file, and importing into OneStream, then doing the same in reverse for any allocations or top-side adjustments that had to make it back to the GL. We built a bidirectional REST integration that retired the manual hop, then used the rebuild to address two adjacent problems the team had been living with: member-formula and confirmation-rule logic copy-pasted across dozens of workflow-scoped scripts, and report formatting that drifted between whichever consultant or analyst produced the deliverable. The integration was the visible outcome. The shared-script library and the formatting standard were the durable ones.
The Problem
Three problems compounded each other. The integration gap was the loudest, but the rule-corpus drift and the report-formatting drift were the ones the team had quietly stopped trying to solve.
- Manual data hop, every close. An analyst exported a trial balance from Inforgl, ran a reshaping macro, dropped it into OneStream's import area, and triggered the load. Anything going the other way — top-side adjustments and allocations posted in consolidation that legal entities had to see in the GL — went out as a second export, hand-keyed back into Inforgl. The round trip always finished, but it cost an analyst the better part of two days each close and left the two systems out of step in the meantime.
- Member formulas duplicated across rules, with drift. OneStream's member formulas had been written workflow-by-workflow over the platform's lifetime. The same FX translation, the same intercompany elimination, the same ratio derivation appeared in five or six places with subtle variations — a rounding convention here, a different
None-handling branch there. When the business changed a rule, someone had to find every copy. They mostly did. They didn't always. - Confirmation rules in the same shape. A "no negative inventory in unit
X" check existed in three workflows, written three times, with three slightly different messages and one missingAbs()guard that meant one copy silently passed bad data. - Report formatting drifted between deliverables. Each consultant had their own font stack, header conventions, totals-row treatment, and number-format defaults. Side-by-side, two reports from the same team in the same week looked like they came from different vendors. A client lead had flagged this politely twice; nobody disagreed, but nobody owned the fix.
The brief I took on was the integration. The rule-corpus and formatting problems were on the team's wishlist but not in scope. I added them after the integration design landed, once it was clear the work would touch enough rules to make the cleanup pay for itself.
Process
The work ran in three phases, with the rule-library and formatting work overlapping the back end of the integration build.
Phase 1 — Stand up the REST bridge, both directions
The Inforgl REST surface is well-documented but not generous: OAuth2 client-credentials, non-uniform per-endpoint rate limits, and errors returned as a mix of HTTP status codes and inline status fields in the response body. The OneStream side was driven from an Extensibility rule — the right home for anything triggered by workflow events, scheduled by the platform, or kicked off from a dashboard button.
I built the REST client as a single Extensibility rule with a typed wrapper around the four endpoints we actually needed: GL balance pull, journal post, dimension-metadata pull, and adjustment post. The wrapper handled OAuth2 token caching (one fetch, reused until the token's exp claim came due), retry-with-backoff on the rate-limited endpoints, and structured error surfacing — non-success responses were raised as typed exceptions the calling rule could route to the workflow's error channel rather than swallow into a generic "load failed" message.
The outbound direction (Inforgl → OneStream) replaced the export-and-reshape macro. The rule pulled the trial balance for the closing period directly from Inforgl, mapped GL accounts to OneStream's account dimension via a small lookup table, and posted into OneStream's import staging area for the existing validation pipeline to pick up. The mapping table was the only stateful configuration; everything else was derived from live metadata at call time.
The inbound direction (OneStream → Inforgl) was more delicate. Top-side adjustments had to be expressed as journal entries Inforgl would accept — respecting its required-field set, its balanced-journal rules, and its idempotency expectations. We added a "ready to post" gate in the OneStream workflow: an adjustment only fired across once it had cleared confirmation and a reviewer had signed off. The post used Inforgl's journal-create endpoint with a deterministic external reference, so re-running the rule was safe — if the journal already existed against that reference, Inforgl returned the existing record rather than duplicating it.
Phase 2 — Audit the rule corpus, extract the shared library
Once the REST bridge was running, I went back to the rule corpus with a different question: how much of this is actually unique, and how much is the same thing written multiple times?
The audit was manual but not slow. I dumped every member formula and confirmation rule out of the application as text, ran a similarity pass (normalised whitespace, comments stripped, member references replaced with placeholders), and grouped by signature. About 40% of the member-formula corpus collapsed into eight distinct logic patterns. The confirmation-rule corpus was even more concentrated — roughly half of all confirmations were variations on five underlying checks.
I refactored the unique patterns into shared functions in their own rule files: one for the formula library, one for the confirmation library. Each function took its inputs explicitly — no hidden coupling to workflow-local state — and was documented at the top of the file with its replaces-list, inputs, and failure modes. Every place that had held a copy of the logic now called the shared function instead.
The drift problem solved itself once the library existed. When the business changed a rule, there was one place to change. When a new workflow needed an existing check, it imported it. The two analysts most affected by the old pattern took to the library quickly — within a few weeks they were adding new shared functions themselves rather than writing one-off variants.
Phase 3 — Define and roll out a formatting standard
The formatting work was the smallest in scope and the easiest to land. I wrote a one-page standard covering the six things actually varying in practice: font family, header structure, totals-row treatment, number formats (currency, percentage, ratio, count), conditional-formatting palette, and footer block. The standard codified the best of what people were already doing rather than inventing a new house style — short enough that a consultant new to the team could absorb it in ten minutes.
Backing it, I built a lightweight format-lint script that read each report's structure and flagged deviations with a row reference and a suggested fix. The script wasn't a gate; it ran on demand from a dashboard button and produced a checklist the consultant worked through before sending the deliverable out. Advisory rather than blocking — that distinction was what kept the team using it rather than working around it.
Solution
The work shipped as four components, plus the documentation that made them legible to the rest of the team.
1. Inforgl REST client (Extensibility rule)
A single Extensibility rule wrapping the four endpoints we needed. Handles OAuth2 token caching, per-endpoint backoff on rate-limited routes, deterministic external references on writes for safe re-runs, and structured error surfacing into the calling workflow. Outbound and inbound flows ride the same client; only the endpoints and the triggering workflow events differ.
2. Shared formula library
A rule file holding the eight member-formula patterns the audit collapsed the corpus into. Each function takes its inputs explicitly, has a comment block documenting its replaces-list and failure modes, and is referenced by name from every member formula that used to inline the logic. New formulas that don't fit an existing pattern start as one-offs; if the pattern recurs, it gets promoted into the library.
3. Shared confirmation library
Same shape as the formula library, for confirmation rules. Five underlying checks, each parameterised by the member set and threshold the calling workflow needs. The Abs() guard that one of the original copies was missing now lives in the shared function — every confirmation using that check inherits the correct behaviour.
4. Format standard + lint script
A one-page standard covering font, headers, totals, number formats, conditional-format palette, and footer block. A lint script that reads any deliverable and surfaces deviations as a checklist. Short on purpose, advisory on purpose — both choices kept adoption frictionless.
The integration, the two libraries, and the format standard rolled out in that order, with documentation written as I went. The two validating analysts ran the first close on the new bridge in parallel with the old macro and signed off on the byte-level match before the manual flow was retired.
Results
| Metric | Before | After | Change | |---|---|---|---| | Inforgl ↔ OneStream integration latency | Manual cadence, ~2 analyst-days per close | Live via REST, triggered by workflow events | manual hop retired | | Duplicate member-formula instances | ~40% of corpus was repeated logic | 0 — all collapsed into shared library | drift eliminated by construction | | Rule-rewrite time when business logic changes | High — every copy hunted down and updated by hand | Low — one shared function, one change | one place per rule | | Formatting variance across deliverables | Visible side-by-side, flagged by client lead | None — standard + lint script | consistent across the team |
Soft outcomes:
- Onboarding got faster. Analysts who joined after the library landed were productive on rule changes inside their first week — the library was self-documenting and the patterns were obvious from the file. The previous ramp had taken a month of "ask the person who wrote it" cycles.
- "Why does this rule do this?" investigations dropped sharply. When every variant looked slightly different, every investigation started with a diff exercise. With one shared function per pattern, the question collapsed to "what does this function do?" — and the answer was in the comment block.
- The library became the team's default pattern. New work started with "is there a shared function for this?" rather than "let me copy the rule from the last project." That cultural shift was the outcome I cared about most.
Learnings
What worked. Treating the integration as the entry point and the library refactor as the durable win. The REST bridge was the visible deliverable and the thing the client asked for, but the audit and library extraction were what made the codebase maintainable for the team that owned it after I left. Integration first to earn the credibility, refactor second once the touch-points were already on my desk — the right sequence.
What I'd do differently. Run the rule-corpus audit at the start of the engagement rather than after the integration shipped. I had the data on day one; doing the audit early would have shaped the integration's own structure and saved me one round of "wait, this logic already exists somewhere" mid-build. The audit is cheap and the answer is almost always actionable — no reason to defer it.
Skill developed. Reading a rule corpus as a similarity-grouping problem rather than as a list of files. The pass is mechanical once you normalise the surface noise, and the groupings tell you exactly where the team's pain is concentrated. That technique now shapes how I scope refactor work on every engagement: before proposing a library, prove the duplication.