8Examples / work
Case study · Streamline Landlord Service

Ink arrives as a webhook.

A multi-party DocuSign signing workflow for a property-management SaaS: leases, condition reports, and maintenance contracts, signed by landlords, property managers, and any number of tenants, with every callback landing as a fact in an event-sourced ledger.

Streamline Landlord Service
Streamline Landlord Service
Winnipeg, Canada
The Product

A rental platform where the paperwork is the product.

Streamline Landlord Service, founded in Winnipeg by Todd Soura and Allan Redman, is a one-dashboard platform for Canadian landlords: rent collected by pre-authorized debit, tenants screened in seconds, maintenance tracked, books synced. Its promise is in its name; the theme is that landlords should stop chasing paperwork. Which made the one remaining stack of actual paperwork, the signing of legal documents, the feature that had to feel the most effortless and be the most correct.

A lease is not a form you fill in; it’s an agreement between several people, executed in a specific legal sense, at a specific moment. 8Examples was brought in to build that: legally binding e-signatures powered by DocuSign, woven into Streamline’s workflows so that a landlord sends a lease the way they’d send a message, and the system knows, reliably and provably, when it has been signed by everyone who needed to sign it.

The Tangle

Several people, several papers, and news that arrives whenever it likes.

Three things make signing harder than it looks. First, the cast varies: a lease might be landlord and tenant, or a property manager signing on an owner’s behalf opposite two, three, four co-tenants; condition reports and maintenance contracts each have their own cast. Second, the paper varies: different templates, different fields, different rules about who must sign for the document to mean anything.

Third, and this is the one that breaks naive designs: the moment you hand an envelope to DocuSign, the truth leaves your system. Signatures happen out there, on someone’s phone at a kitchen table, and the news comes back as HTTP callbacks: webhooks that arrive late, arrive twice, or arrive in an order nobody promised. Most CRUD systems meet this with a status column and a prayer.

The First Move

DocuSign is just another user.

We modeled the signing timeline the way we model everything: screens, commands, events, read models, laid out over time until everyone could point at it. The move that untangles the whole thing is treating DocuSign like a person. Its webhooks walk in through the same front door as a human click, issue ordinary commands, and those commands append ordinary facts. Nothing about the outside world’s chaos leaks past the threshold.

Here is one lease with two tenants, from send to executed:

SCREENS &AUTOMATIONCOMMANDSEVENTS(the ledger)READ MODELStime01 · send for signinga lease, two tenantsSendForSignaturesigning_requestedtemplate + recipientsauto · envelope jobhands the paperworkto docusignRecordEnvelopeSentenvelope_sentenvelope id keptContractsToSendwebhook · signedtenant 1, on her phone,docusign calls backRecordRecipientSignedtenant_signed1 of 2webhook · signedtenant 2, a day laterRecordRecipientSignedtenant_signed · 2 of 2lease_executedthe fold noticed it was the last hand02 · status boardexecuted · both namesSigningStatusa fold over historywebhook · declinedthe other endingRecordDeclineenvelope_declineda fact, not an error
screenautomationcommandeventread model
Read it left to right; that axis is time. Each signature is its own gold fact, appended by a webhook-triggered command. lease_executed isn't a status flag anyone sets; it's appended when the fold over the ledger notices the last required hand has signed. And a declined envelope isn't an error path; it's just a different fact.

Notice what doesn’t exist: no status column being overwritten as the envelope moves, no reconciliation job diffing our database against theirs. The ledger records what happened; the current state is derived, on demand, from the whole story.

The Shape

An envelope is an aggregate; its history is the only state.

Under CQRS with event sourcing, each signing workflow is an aggregate: a stream of facts from signing_requested onward. Commands replay the stream, check the guard, and append; queries replay the stream and answer. Whether a lease is “waiting on tenant 2” is not stored anywhere. It is computed from the facts every time someone asks, which means it is never stale and never disagrees with the history that produced it.

The DocuSign envelope id is recorded in envelope_sent, tying each callback to its aggregate. From there, every piece of news from their cloud, delivered, signed, declined, voided, is one more append. The write side is a few small command handlers; the interesting engineering is in what they refuse to do.

3
contract types, one machine
1–N
signers per envelope
≥1
deliveries per webhook, by design
0
status polls to DocuSign
0
UPDATE statements
The Callback

The webhook that can arrive twice, safely.

DocuSign’s delivery contract is at-least-once: if their POST times out, they send it again. A system that increments counters or flips status flags corrupts itself under that contract. A system that appends facts doesn’t, because recording that tenant 2 signed is idempotent the moment you check the ledger before you write: if the fact is already there, there is nothing to do, and nothing to do is a perfectly good outcome.

docusign(their cloud, their retries)webhook endpoint(streamline api)events.db(append-only)POST /webhooks/docusign · recipient-completedhmac signature in the headerverify the hmac · docusign or nobodyreplay this envelope’s eventsrequested, sent, tenant 1 signedguard: is tenant 2’s signature news?append tenant_signed · 2 of 2append lease_executedthat was the last hand200alt · the same webhook, againPOST · recipient-completed, identicala timeout on their side, a redeliver on oursreplay…tenant 2 already signed, lease already executedappend nothing200acknowledging a fact twice is freeat-least-once delivery is docusign’s contract · idempotency is ours, and the ledger provides it for free
The first delivery appends two facts; the second delivery appends none. Both get a 200, because both were successfully processed; DocuSign doesn't need to know how boring the second one was. The HMAC check at the top is the other half of trusting a webhook: facts are only accepted from callers who can prove they're DocuSign.

Out-of-order arrival gets the same treatment. The guard doesn’t assume webhooks tell a tidy story; it replays what the ledger knows and decides whether this piece of news is new. The chaos of distributed delivery is absorbed in one place, at the threshold, and everything behind it stays sequential and calm.

Many Hands

Three kinds of paper, any number of signers, one machine.

The variety that looked like the hard part, different contracts, different casts, turned out to be data. A signing workflow is a template plus a recipient list; the aggregate does not care whether the second signer is a co-tenant or a contractor. When the fold sees that every required recipient has a signed fact, the document is executed. Two tenants or four is the difference between N=2 and N=4, and no code knows the number.

residential leaselandlordtenant 1tenant 2…or three, or four; N is datacondition reportproperty managersigning for the ownertenantmaintenance contractproperty managercontractorthe same machinesigning_requestedenvelope_sentrecipient_signed · one per handexecuteda new contract type is a template and a recipient list · the aggregate, the webhooks, and the fold never change
Different papers, different casts, identical machinery. The recipient list travels inside signing_requested, so adding a contract type, or a fourth co-tenant, changes an input rather than the system.
The Payoff

When a signature is disputed, you read; you don't investigate.

Signing is the part of a rental platform where correctness has a legal edge. The ledger gives Streamline a story it can stand on: requested at 9:02, sent at 9:02, tenant 1 signed at 9:40, tenant 2 signed the next afternoon, executed the same second. That account agrees with DocuSign’s own audit certificate, because both were built from the same events; ours simply never left the building.

And the operational wins compound quietly. Webhook retries are non-events. A support question about a stuck lease is answered by reading the stream, which usually ends with “tenant 2 hasn’t opened the email.” New paper, like the next contract type the product grows, rides the machinery that already exists. The feature shipped as a workflow, but what Streamline actually gained is a fact-keeping habit at the exact spot in the product where facts are the product.

The Point

The hard part wasn't DocuSign. It was deciding what's true.

Integrations get called plumbing, and plumbed is how they usually feel: a tangle of callbacks patching a status column. Walking the timeline first turned this one into a model instead. Who acts, what they ask, what becomes true, who needs to know; once those were drawn, DocuSign shrank to one automation lane in a picture everyone at Streamline could point at and say yes, that’s what signing is.