Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

How E-Signing Works in a Property Management System

22 Feb 2025Category : Blog

About Post

From the tenant's side, signing a contract takes about as long as liking a photo. Open it, scroll, tap "Sign". Done.

From the system's side, that tap is the easy part. The hard part is being able to answer, months or years later, a few uncomfortable questions with total confidence: Who signed this? When? Which version? Were they allowed to? Has anyone changed it since?

Kate PMS, the property management system I lead the engineering on at Kate, handles more than 2,000 tenancy contracts, with contracts and e-signing as one of its core features. I won't share internals, and the examples below are simplified, but I can walk through the shape of the flow and the principles behind it. They're the same in any system that signs things.

A contract is a state machine

The first design decision, and the most important one, is to treat a contract as something that moves through clear states, with rules about who can move it and when. Roughly:

  1. Draft: staff prepare the contract. Terms, unit, dates, rent. Everything is editable.
  2. Review: someone with the right role checks it. Edits send it back to draft.
  3. Ready to sign: the content is frozen. This is the moment the document stops being "a form" and becomes "the thing people will agree to".
  4. Signed: all required parties have signed. The final document is generated and locked.
  5. Later states like active, renewed or terminated follow the lease itself.

Writing transitions down explicitly, instead of letting any screen update a status column, prevents a whole class of bugs. A simplified version of the idea:

enum ContractStatus: string
{
    case Draft = 'draft';
    case Review = 'review';
    case ReadyToSign = 'ready_to_sign';
    case Signed = 'signed';

    public function canMoveTo(self $next): bool
    {
        return in_array($next, match ($this) {
            self::Draft => [self::Review],
            self::Review => [self::Draft, self::ReadyToSign],
            self::ReadyToSign => [self::Draft, self::Signed],
            self::Signed => [],
        }, true);
    }
}

Notice that Signed goes nowhere. A signed contract is never edited. If the terms change, that's an amendment or a new contract, with its own trail.

Freeze before you sign

The single most important rule in any signing flow: people must sign exactly what they saw, and that thing must not change afterwards.

That's why "ready to sign" exists as its own state. Once a contract reaches it, the content is locked. If someone needs to fix a typo, the contract goes back to draft, and anyone who was about to sign sees the new version, not a silently edited one.

Verifying who is signing

A signature is only meaningful if you know the signer is who they claim to be, and is entitled to sign. Users authenticate in the usual way, and role-based access controls who can prepare, review and send contracts.

For company tenants there's an extra step that I find one of the more interesting parts of the system: contract signing is verified against Commercial Registration records. The company's details are checked against its official registration before the contract can be signed. Catching a mismatch at that point is far cheaper than discovering it during a dispute.

The audit trail: write it like a diary, not a whiteboard

Every meaningful event in a contract's life gets recorded: created, edited, sent for review, approved, viewed, signed, downloaded. Each entry captures who did it, what they did, when, and relevant context about the request.

The key property is that the trail is append-only. Entries are added, never updated or deleted. A whiteboard can be wiped and rewritten; a diary with numbered pages can't, without it being obvious. When a question comes up later, the answer comes from the trail, not from someone's memory.

In Laravel terms that usually means a dedicated table written from one place in the code (often an event listener), no update or delete paths in the application, and timestamps stored in UTC so the order of events is never ambiguous.

The test I use: if a contract were disputed tomorrow, could we reconstruct its full story from the system alone, without asking anyone what they remember? If not, something is missing from the trail.

PDF integrity: proving nothing changed

When the contract is signed, the final PDF is generated. That file is the artefact people will rely on. So how do you prove later that the PDF in front of you is the one that was signed?

The standard technique is a cryptographic hash. You compute a fingerprint of the file at signing time and store it with the signing record:

$hash = hash_file('sha256', $pdfPath);

$contract->signature()->create([
    'document_hash' => $hash,
    'signed_at' => now(),
]);

Change a single character in the file and the SHA-256 hash changes completely. Verifying later is simple: hash the file again and compare. If they match, the document is the one that was signed. If they don't, you know immediately, and you know it's not the original.

The file itself goes into private storage, never a public URL, and is served only through an authorised download route.

What I'd tell anyone building this

  • Model the lifecycle as explicit states and allowed transitions.
  • Freeze the content before anyone signs, and never edit after.
  • Verify identity and authority before the signature, not after.
  • Keep an append-only audit trail of every meaningful action.
  • Hash the final document and keep the hash with the signing record.
  • Store signed files privately and serve them through access checks.

None of this is glamorous. Nobody will ever compliment the audit table. But the day someone asks "can you prove it?", it's the most valuable part of the system.

If you've built a signing or approval flow, what was the part that turned out harder than you expected?

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close