15 January 2026 / Platform delivery

Reconciling portals, permissions and integrations before release

In a multi-portal product, the last release checks tend to fail in the gaps between components: a role sees the wrong state, an integration updates late or two screens apply different rules. The examples here are a composite of problems that keep coming up in delivery.

Intersecting steel beams and bracing viewed from beneath a bridge.
Photo: Sebastian Schuster (opens in a new tab)

In a multi-portal product, every page can work fine on its own and the product can still be wrong. The admin portal accepts a change, the customer portal shows the old value, and a partner role can see the record but not the action it needs to move it along. None of that belongs to one screen, so isolated acceptance checks tend to miss it. (The examples here are a composite of problems that keep coming up in delivery, not any one project.) The check I find most useful follows one record through the whole product: create it the way a real user would, move it through each permitted state, let the integrations run, then look at what every affected role can see and do.

I'd use a small set of deliberately different test records: one ordinary path, then a declined action, a delayed integration, a corrected field and a user with limited permissions. For each one, write down:

  • its current state and the event that put it there
  • which role owns the next action
  • which portals can display it
  • which fields can be edited at that point
  • which external update is expected, and how late it's allowed to be
  • what evidence should be left once the work's done

Permission testing usually stops at whether a role can open a page or see a button, and that leaves a big gap. The interface can hide a control while the endpoint still accepts the request. So I build the role matrix around actions on a record in each state (view, create, amend, approve, reject, reopen, export, delete), including links, bulk tools and APIs, and test both sides: the allowed action works and the blocked one gets rejected by the server. If something's unavailable because another role has to act first, the user should be told that, not shown a generic error.

The other common mess is state. One screen treats a submitted record as complete, another waits for an external acknowledgement and a third relies on a nullable date nobody updates on failure. Name the states and the events that move between them, and keep a transitional state like "awaiting external confirmation" only if staff actually need to wait or step in. For integrations, keep "hasn't run" separate from "ran and failed" (queued, sent, acknowledged, rejected, timed out, retrying), and test duplicate, late and out-of-order delivery. A second copy of an event shouldn't repeat the business action, an older status shouldn't move the record backwards, and a successful retry shouldn't erase the failed attempt from history.

The release review should end with named evidence: the record IDs, expected states, actual results and unresolved differences from running those fixtures against the release candidate with production-like roles and integration settings. I wouldn't roll that up into a percentage, because a blocked path hides inside an average. A broken permission boundary or an integration state you can't recover from needs a decision before release, while a cosmetic mismatch can be logged and scheduled. For each fixture, can every role see the right state, take only the allowed action and understand what happens next? If answering that means opening a database console or asking which portal is right, there's still reconciliation work to do.

The admin portal accepting a change while the customer portal still shows the old value is exactly the kind of gap that slips through. Each page passing its own checks doesn't tell you the product is right, and following one record through every role and integration is what shows whether the parts agree. Before signing off a release, I'd run those fixtures against the release candidate and only move on once each difference is either fixed or has a decision recorded against it.

Got a take on this?

Share it on X. I read the replies.

All blogs