Segregation of duties: enforced, not a policy document

Ask a legal or finance team whether they have a segregation-of-duties rule and the answer is almost always yes. It sits in an approval policy, a delegation-of-authority document, or an internal controls handbook: the person who approves a contract should not be the same person who signs it.

Ask how the rule is applied and the answer gets quieter. Usually someone reads the approval trail before signature and notices, or doesn’t. The rule exists, but it is only as reliable as the attention of whoever happens to be looking that day. A busy week, a small team, or a contract that moved quickly can all let the same name appear twice.

The difference we care about is between a rule people are expected to follow and a rule the system will not let them break. This post describes the second kind, as it works in Contrayo today.

What the engine blocks

Contracts in Contrayo are routed through the policy in effect, which your organization defines: ordered steps, each with named people who may act on it. A step is an approval, a signoff, or a signature. When the policy has segregation of duties switched on, the engine refuses one combination: the same person recording an approval or signoff on any step of a contract’s routing and also signing that routing’s signature step, in either order.

The refusal happens on the server, at the moment someone tries to record the action. It comes back as a 403 with a message that names the reason. The same check runs whether the action arrives from the Contrayo web app, from a Slack button, or from the public API, because all three go through the same function. We say this because a control that lives only in the interface is a control that anything else can walk around.

The check also looks past who clicked. If A delegates to B, A approves a step, and B then signs as A’s delegate, the engine treats that as one person doing both halves and refuses it. The reverse arrangement is refused as well.

An API key can also only record those decisions as the person who created it, so an administrator cannot mint a key that acts as a colleague.

Every approve, reject and sign decision is stored with the person who acted and, when they acted under a delegation, the person whose slot they filled.

Where an organization can switch it off

The setting belongs to the policy version and is on by default. An organization can author a new policy version with it off, which suits a very small company where one person genuinely holds both roles. Policy versions are never edited in place: a change creates a new version with an effective date, and each routing keeps the version it was created under (a fresh re-route uses the version in effect then). So turning the rule off changes how future routings are checked and leaves the history of earlier ones intact.

There is no override on an individual contract. An administrator cannot approve past the block for one signature while leaving the rule on; the only route is a different policy version.

What it does not cover

This is the part to read before relying on it.

The rule is narrow. It prevents the same person from both approving and signing within one routing. It does not stop one person from satisfying two approval steps, so if your policy lists the same individual on both the legal review and the finance review, nothing in the engine objects.

It does not stop the person who drafted or uploaded a contract from approving it. Contrayo does not currently compare a contract’s creator against its approvers, so an organization that wants a “requester cannot approve” rule has to build it into who it lists on each step.

It applies only when the policy a contract is routed under has the setting on. And it works on the identities in your system: it cannot see that two accounts belong to the same human if someone creates a second account for themselves, which is a user-administration question the engine does not answer.

On a resubmission after a rejection, the rejected step and every step after it reset, and approvals on those steps no longer count. Steps that cleared before the rejection keep their approvals, and those still count toward the rule.

Why bother with the structural version

A written policy tells people what is expected. It has no way to catch the moment someone is rushed, unaware or tempted. A control that refuses the action at the point of signing does not need anyone to be watching, and it leaves a record that the refusal is how the system is built.

If your Legal team is the audience for this control, the Legal solution page shows where it sits among the rest of the approval workflow. Start there, and treat the limits above as the checklist of what you would still need to cover in how you name approvers.

← Back to the blog