"We Never Approved That"

A thumbs-up emoji is a record of sentiment, not of content. When the marketing director who approved the work leaves, sentiment evaporates — and a 64-character string is what is left standing.

Rowan Petrie · Commercial photography · Featured capability: Email-verified sign-off and public verification

Results at a glance

  • 48 hrs — to close a dispute that previously ran nine weeks
  • AUD 11,500 — invoice paid on its original terms
  • ~30 hrs — annual re-edits no longer performed for free

Composite scenario. The person named here is not a customer: the protagonists are composites drawn from real usage patterns, and the figures are proportional to a solo freelancer or small studio rather than measured from one account. Every mechanism described is implemented in the product as written.

The studio

Rowan Petrie photographs buildings and interiors for property developers, architecture practices and two hotel groups, working out of Melbourne with one retoucher on contract. A typical job is 30 to 60 finished images licensed for a defined use and a defined term. The approval step matters more in his work than in most: retouching is subjective, expensive, and once a campaign ships, "we would like a different grade" means redoing work that has already been paid for once.

The breaking point

A developer's marketing manager approved a 40-image edit set the way clients always do.

From: Steph, Marketing Manager

"These look great — go ahead. 👍"

Nine weeks later the campaign was live, Steph had moved to another company, and a new marketing director wrote:

"I've reviewed the deliverables against the brief and I don't see any record that the retouching approach was signed off. The interiors read too warm for our brand palette. We'd need these reworked before we can process the invoice."

Rowan went looking for his evidence and found none worth the name. He had a forwarded email chain with no version reference in it. He had a shared gallery whose contents he had replaced twice during that same fortnight, so the link he had originally sent no longer showed what Steph had actually seen. He had an approval, and no way to prove what had been approved.

The invoice sat for nine weeks. He absorbed eleven hours of re-grading to keep the relationship, and the part that stayed with him was not the money. It was discovering that his entire proof of a year's client approvals was a set of email threads that referenced nothing specific.

How Stria solved it

On his next job for the same developer, Rowan sent the contact sheet through a Stria review link: a /share/<uuid> URL, no account, no password. That link is the whole access model, and it is stricter than it looks. The anonymous database role holds zero table privileges, and every read a client makes goes through a security-definer function scoped to exactly one id. There is nothing to enumerate and no login to abandon.

The client marked up the PDF directly with pins, boxes and text highlights, and each comment was filed as either minor polish or structural reversal. Their original choice is preserved write-once, and Rowan can correct the effective label when a client tags a full re-grade as "minor" — the classification the product acts on is his, not the counterparty's. Every published iteration froze into an immutable snapshot carrying a SHA-256 content_hash plus the previous snapshot's hash. That is a real chain, not a per-row checksum.

Then the sign-off. The client entered their email and received a six-digit code generated from a cryptographic RNG. That code is never stored. What is kept is an HMAC-SHA-256 of the code, the deliverable id and the email, keyed with a server-side pepper held outside the table entirely. If that table alone ever leaked, a six-digit code would still be unrecoverable offline, because the attacker would not have the pepper — and if the pepper is missing when a peppered row is checked, verification fails closed rather than quietly downgrading to something weaker. The code expires in ten minutes, allows five attempts, is single-use, and the deliverable row is locked while it is consumed so two concurrent approvals cannot both succeed.

What the record then captures, column by column:

  • the verified email the code was sent to and consumed from
  • the server-observed IP address, taken from the request rather than the form
  • a bounded copy of the user agent
  • the exact consent sentence displayed to the signer
  • the version number approved
  • a SHA-256 fingerprint computed by one canonical, NULL-safe function over the deliverable id, version number, title, embed URL and asset path — so the fingerprint covers the delivered file, not just its label

Then the record closes. BEFORE UPDATE and BEFORE DELETE triggers raise for everyone, including the table owner and definer functions, and a further trigger refuses outright to insert a sign-off with no fingerprint. A signed deliverable cannot be deleted at all. Nobody at Stria and nobody in Rowan's workspace can edit an approval after the fact, which is the only version of "immutable" worth printing on a certificate.

The certificate PDF prints all of it, and one thing more: a link to a public /verify/<hash> page. When the next dispute arrived — and one did, over a hotel shoot eight months later — Rowan did not argue. He forwarded the certificate. The client's own legal reviewer pasted the fingerprint into the verification page, with no account and no relationship to Stria, and got back the subject, the organisation, the signatory's name, the timestamp, and a field called content_intact that is re-derived on the spot. The chain is recomputed from the current row contents, so a record altered after signing reports broken even though the sign-off row still carries its original hash.

The page is deliberately narrow about what it will do. It accepts only a full 64-character hash and refuses prefixes rather than partially matching them, distinguishes "that is not a fingerprint" from "that fingerprint is not on the record", returns a uniform negative with no near-miss or count, is rate limited per IP, and never returns the signer's email or IP address. It confirms a certificate without becoming a way to enumerate anybody else's.

Rowan also noticed what the certificate does not claim. It names the mechanism — a single-use emailed code, hashed, ten-minute expiry — and then states plainly that this is a Simple Electronic Signature under eIDAS, not an advanced or qualified signature. He said that sentence is the reason he trusts the rest of the document.

Results and business impact

The hotel dispute closed in 48 hours. The AUD 11,500 invoice was paid on its original terms, with no re-grade and no discount.

Across the following year Rowan logged zero re-edits performed for free, against three the year before — roughly 30 hours recovered. He now attaches the certificate to every final delivery as a matter of course, and two clients have referenced it in renewal conversations as evidence he runs a tight process.

The commercial shift was in what he felt able to quote for. Rowan had been avoiding multi-stage campaign work with large marketing teams, precisely because staff turnover mid-project was where his approvals evaporated. He took two of those jobs in the following year at roughly double his usual project value.

Why this feature matters

Most approvals live in email, and email is a record of sentiment rather than of content. When a client changes staff — which is exactly when disputes happen — sentiment evaporates and you are left holding a thumbs-up attached to nothing in particular.

A hash bound to a frozen version, a verified email, a server-observed timestamp and a page a stranger can check turns "they approved it" from a claim into a fact a third party can confirm without taking your word for anything. That is a different negotiating position, and it costs nothing extra at the moment of delivery.

Try it yourself

The email-verified sign-off and the public verification page are on the free plan — start a 14-day trial at getstria.com, sign off one deliverable, download the certificate, and paste the fingerprint into the verification page from a browser you have never logged into. That is the whole demonstration, and it takes about four minutes.

More case studies

Start your 14-day trial · Try a free scope check · All case studies