Subscribe to get high-signal insights on how modern fintech is built.

engineering

The Maker-Checker Pattern for Fintech Ops

RBAC confirms the person has the right role, but it can't confirm the action is correct.

By Alex Kugell ·

Your admin panel has a button that changes fee percentages for every institution on the platform. Another button overrides an OFAC sanctions screening. A third triggers a settlement batch that moves real money between real bank accounts.

Every one of those buttons works exactly as designed. The person clicking them has the right role. They passed authentication. RBAC says yes.

But RBAC answers a narrow question: does this person have permission to do this? It doesn't answer the question that actually matters in a financial system: should this specific action happen right now?

That gap between "permitted" and "correct" is where fintech incidents live. And the pattern that closes it has been around longer than most of the software we build on top of it.

What RBAC actually covers

A typical admin system has three roles. A viewer can inspect but not change. An operator handles routine management. An admin can modify system configuration.

This works. A viewer can't accidentally change a fee percentage. An operator can't reassign admin roles. The hierarchy prevents people from doing things outside their job.

But consider what happens inside a single role. An admin with full privileges sits down and does any of the following:

  • Overrides a charity's OFAC screening status from "flagged" to "clear"
  • Changes an institution's fee percentage from 3% to 4%
  • Triggers a fee settlement batch outside the normal schedule
  • Disables safeguard evaluation for a specific institution

Each action is legitimate. Each one is also a potential compliance violation, a financial error, or the kind of mistake that generates an incident report. RBAC can't distinguish between the admin who meant to change the fee from 3.0% to 3.1% and the one who fat-fingered it to 30%.

RBAC operates on identity and role, so it has no opinion about whether a specific action, at a specific moment, with specific parameters, is a good idea.

The second pair of eyes

Maker-checker splits every sensitive action into two steps. One person initiates the action (the maker). A different person reviews and approves it (the checker). The system doesn't execute until the second approval arrives.

Think of it like a construction site inspection. The electrician wires the panel, and a separate inspector signs off before the power gets turned on. Even if the electrician is the best in the city, the inspection still happens, because a wiring mistake can burn down the building.

In a fintech admin panel, the flow looks like this. An admin requests a fee change from 3% to 4% for Institution X. The system writes the request to a pending queue and notifies a second admin.

That second admin opens the request, sees the current value (3%), the proposed value (4%), and the institution it affects. They approve or reject.

Only on approval does the system execute the change.

For a two-person ops team, this might be a Slack notification and a button in the admin UI. For a larger organization, it's a formal workflow with SLAs, escalation paths, and audit trails.

The heuristic: which actions need it

Not every admin action needs a second approval. Adding a new customer to the platform is routine. Running a standard report is routine. The overhead of maker-checker on every click would grind operations to a halt.

So you need a filter. The heuristic I use has three criteria. If an action can cause any of these, it gets maker-checker:

Direct financial harm. Changing fee percentages, triggering settlement batches, modifying payout schedules, adjusting account balances. Anything where a wrong value moves real money in the wrong direction.

Compliance violation. Overriding sanctions screening, modifying KYC verification status, disabling transaction monitoring for an entity. These actions create regulatory exposure that can result in fines, consent orders, or worse.

Data integrity corruption. Manual ledger interventions, bulk data modifications, overriding system-generated calculations. Once ledger data is wrong, every downstream report, audit, and reconciliation inherits the error.

If an action doesn't hit any of these three, it's probably safe to execute on a single approval. If it hits two or three, consider requiring more than one checker.

What the approval queue looks like

The older pattern embeds approval logic into every sensitive table. A fee_changes table gets approved_by and approved_at columns. A screening_overrides table gets the same. Every table that holds a sensitive action grows its own mini-workflow.

This works until you have fifteen sensitive operations spread across eight tables. Now your approval logic is scattered, your audit trail is fragmented, and adding a new sensitive action means modifying a schema.

The better architecture is a centralized approval queue. One pending_actions table sits between the request and the execution.

CREATE TABLE pending_actions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  action_type TEXT NOT NULL,
  payload JSONB NOT NULL,
  requested_by UUID NOT NULL REFERENCES users(id),
  requested_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  reviewed_by UUID REFERENCES users(id),
  reviewed_at TIMESTAMPTZ,
  status TEXT NOT NULL DEFAULT 'pending',
  CHECK (status IN ('pending', 'approved', 'rejected', 'expired')),
  CHECK (requested_by != reviewed_by)
);

The last constraint is the most important. The person who requests an action cannot be the person who approves it. Without this, a single admin can approve their own changes, and you've built a maker-checker system that doesn't actually check anything.

The payload column holds the serialized action: the target entity, the old value, the new value, any context the reviewer needs to make a decision. When the checker opens the request, they see the full picture, not just "fee change requested."

When an action is approved, a background worker picks it up, deserializes the payload, and executes it. The execution is logged separately, so you have a complete chain: who requested, who approved, when it executed, what changed.

The state machine

A pending action moves through four states: pending, approved, rejected, or expired. Expiration matters because a fee change requested on Monday might not make sense by Friday. Actions shouldn't sit in the queue indefinitely.

The transitions are:

  • Pending to approved: A different user with the appropriate role reviews and approves. The system executes the action.
  • Pending to rejected: A reviewer declines. The requester is notified with the reason.
  • Pending to expired: The action sits unclaimed past a configurable TTL. 24 hours is a reasonable default for most operational actions, shorter for anything time-sensitive.
  • Approved to executed: The background worker runs the action and records the result.

No other transitions are valid. An approved action can't be unapproved. A rejected action can't be resurrected. If the requester still wants the change, they submit a new request. This keeps the audit trail clean: every row in pending_actions represents one complete decision, not a series of back-and-forth edits.

Approval Queue State Machine
different user approvesreviewer rejectsTTL expiresworker executesPendingApprovedRejectedExpiredExecutedrequested_by != reviewed_by enforced at every transition

When maker-checker fails anyway

In August 2020, Citibank accidentally wired $900 million to Revlon's lenders. The bank had a multi-reviewer approval process, and multiple people signed off on the transaction, but it still went wrong.

The failure was the quality of the checking, not the absence of maker-checker. The reviewers anchored to the first approver's judgment instead of independently verifying the transaction details. The system had the right structure but the wrong behavior: the second pair of eyes wasn't actually looking.

This is the failure mode you can't solve with software alone. A maker-checker system prevents a single person from executing a sensitive action, but it can't prevent two people from making the same mistake. But it does shift the probability: two people independently making the same error on the same action is considerably rarer than one person making it alone.

The practical mitigation is to make the review interface explicit. Don't just show "approve/reject." Show the current state, the proposed change, a diff of what will change, and force the reviewer to acknowledge specific details before the approve button activates. Make it harder to rubber-stamp.

The audit trail

In a SOC 2 audit, the examiner wants to know what prevents a single person from doing something harmful.

Maker-checker gives you a concrete answer. Every sensitive action has a request record, an approval record, and an execution record. The audit trail shows who requested the change, who reviewed it, what the change was, and when it took effect. If an action was rejected, that's recorded too, along with the reason.

The trail should be append-only. No updates, no deletes. If an action was approved and later found to be wrong, the correction is a new action that goes through the same approval flow. The original approval record stays in the log, because an auditor needs to see what happened, not a sanitized version of what should have happened.

For retention, SOX requires auditors to keep workpapers for seven years. Most fintech companies default to the same window for their own operational records regardless of whether they're SOX-bound, because the cost of storing a few million audit rows is negligible compared to the cost of not having them when a regulator asks.

Sizing the overhead

The pushback on maker-checker is always speed. If every fee change needs a second approval, operations slow down. For a two-person team where the other admin is in a different timezone, a 30-second action becomes an 8-hour action.

This is a real cost, but count your sensitive actions per week. They're rare and high-impact by definition. If you're executing hundreds of fee changes or sanctions overrides per week, the problem isn't maker-checker overhead. Your system shouldn't need that many manual interventions.

For the genuinely time-sensitive cases, build an escalation path. If the primary checker doesn't respond within 30 minutes, notify a backup. If the backup doesn't respond within an hour, page the on-call. The queue shouldn't be a dead end.

Where RBAC and maker-checker meet

These two patterns aren't alternatives. They're layers. RBAC controls who can even see the maker-checker queue. An operator role might be able to request actions but not approve them. An admin role can do both, but the self-approval constraint means they still can't approve their own requests.

The combination handles two different threat models. RBAC stops the outsider who lacks the right role from performing a privileged action, while maker-checker catches the insider who has the right role but performs the wrong action, whether by mistake or intent.

Your SOC 2 auditor will ask both how you prevent unauthorized changes and how you prevent authorized mistakes. RBAC answers the first, the approval queue answers the second. If you only have one answer to both questions, you have a gap.

Sources

Frequently Asked Questions

What is the difference between RBAC and maker-checker in financial systems?
RBAC controls who can perform an action based on their assigned role. Maker-checker controls whether a specific action should execute by requiring a second person to review and approve it. A fintech system needs both because RBAC prevents unauthorized access while maker-checker prevents authorized mistakes, like a single admin changing a fee percentage or overriding a sanctions screening flag.
Which fintech operations need maker-checker approval?
Any operation that can cause direct financial harm, trigger a compliance violation, or corrupt data integrity. Fee percentage changes, OFAC screening overrides, manual ledger adjustments, and settlement batch triggers outside normal schedules all qualify. Routine operations like onboarding a new customer or running a standard report typically don't need a second approval.
How do you implement a maker-checker approval queue in production?
Use a centralized pending_actions table with columns for the action type, serialized payload, requesting user, status, and timestamps. When a sensitive action is requested, the system writes it to the queue instead of executing it. A second authorized user reviews and approves or rejects it. Only on approval does the system deserialize the payload and execute the action. This is simpler and more auditable than embedding approval columns into every sensitive table.
Why did the Citibank Revlon wire transfer fail despite having multiple reviewers?
Citibank sent $900 million to Revlon's lenders by accident in August 2020 because the reviewers anchored to the first approver's judgment instead of checking independently. The system had a multi-reviewer workflow, but the checkers rubber-stamped the action. The incident shows that maker-checker fails when the second reviewer doesn't perform genuine independent verification of the action's details.
How does maker-checker help with SOC 2 compliance audits?
SOC 2 auditors evaluate whether controls prevent a single individual from executing harmful actions. An append-only audit log showing who requested each sensitive action, who approved it, and when it executed provides direct evidence for the security and processing integrity trust criteria. Without this separation, the auditor's question shifts from 'who approved this' to 'what prevented one person from doing this alone,' and the answer is nothing.

Built by Trio, a fintech-native engineering partner helping teams build the next generation of financial technology and infrastructure.

Subscribe to Ledger Drift for high-signal insights into how modern fintech is built, from systems to code to teams.

Keep reading

anatomyWhy Your Bank Spends More on Workarounds Than on BankingMid-market banks spend more on technology every year only to lose more customers to neobanks.
analysisTokenized Collateral Is Live and the Plumbing Is Already SplittingBNY completed the first tokenized Treasury triparty repo with Citadel, and five other US institutions launched competing settlement platforms in the same quarter.
analysisThe Stablecoin Stack for EngineersA dollar-backed stablecoin touches six layers of infrastructure between mint and redemption, and most of them have nothing to do with a blockchain.
View more ›