I wrote a while back that out-of-home advertising’s structural advantage is reality: you can’t bot a billboard, you can’t fake a human walking past a physical screen. That’s the whole pitch for FameCake, the platform I’m building to let anyone book a digital billboard from their phone.

So it’s a little embarrassing that the first version of my growth loop rewarded the one metric my own users could fake from their couch. It is not the first feature I have torn out of FameCake after shipping it, and it will not be the last.

The Loop I Wanted

The idea is good. You pay for a placement on a few screens you pick. If your ad earns attention, the system rewards you with free reach: extra copies of your creative on nearby screens you didn’t pay for. A paid placement that does well amplifies itself. Spend a little, earn a lot, and the people getting free reach are exactly the ones already invested in the platform.

The question was: what counts as “does well”?

Version One: Likes, and Why They Failed

The first answer was the obvious one. Likes. Every 25 likes on the platform unlocked one more screen, capped at five. The code was trivial: a counter, a threshold, an amplify() call when it tripped.

It was also unsalvageable, for three compounding reasons:

  • On-platform likes are free to fake. A handful of throwaway accounts spam the button and the reward unlocks. There’s no cost to the attacker and no signal in the metric.
  • They measure nothing real. A like on my platform isn’t social virality. It isn’t a sale. It isn’t a human on a street corner. It’s a number that exists only to unlock more of my inventory.
  • I couldn’t even make them real. The honest version would tie reward to social reach: did you actually post this and get traction? But Instagram’s Graph API only exposes mentions for business and creator accounts, and TikTok has no public mentions API at all. My users have personal accounts. The data I’d need to verify a real social signal does not exist for them.
The metric that looks good on the dashboard

Likes were perfect on a slide: simple, automatic, engagement-flavoured. They were worthless in production because they were both gameable and disconnected from any outcome I actually cared about. A reward metric has exactly one job - to be expensive to fake - and this one failed it completely.

Version Two: Proof-of-Post

The rewrite inverts the trust model. Instead of inferring effort from a number I control, I ask for evidence I can check.

The flow:

  1. You pay for your placement. It goes live.
  2. You post your ad clip to your socials, tag us, and share the link.
  3. You submit that link in the app. A pending submission is created.
  4. A human verifies it: the post is public, it tags us, it matches your booking.
  5. On approval, the system amplifies your paid creative onto nearby screens.
  6. If you later delete the post, an admin revokes it and the free screens are torn back down.

The pending human review is the anti-fraud mechanism. There’s no instant gratification to exploit. The reward is held behind a verification step that a bot can’t satisfy, because satisfying it means a real, public, attributable social post exists.

The fix for an unverifiable reward isn’t a smarter detector. It’s redesigning the reward so the thing you’re paying for is the thing you can actually see.

This is the same move as out-of-home advertising itself. The value lives in the part you can’t fake. I’d written that about billboards and then built a reward loop that ignored it. Proof-of-post drags the incentive back onto verifiable ground.

Skin in the Game as the Gate

The quietest design decision is the most important one: you can only amplify a placement you paid for. Free reach is a multiplier on real spend, never a thing you conjure from nothing.

That single constraint kills an entire class of abuse. There’s no “earn free billboards from scratch” path to farm, because the qualification to enter the loop is having already put money down. The incentive is aligned by construction: someone who paid for a placement is motivated to market it well, and marketing it well is exactly the behaviour the reward exists to encourage.

Making It Safe Under Concurrency

Verification gets you honesty. Idempotency keeps the mechanics from corrupting themselves when admins double-click and webhooks retry.

  • Deterministic IDs. Each amplified post gets an ID derived from the booking and screen (boost_<booking>_<screen>). Two concurrent approvals write the same document instead of spawning duplicate free screens.
  • Ordered transactions. Award the screens, run amplify(), then mark the submission approved. If amplification fails midway, the submission stays pending and is safely retried. There’s no half-approved state.
  • Claw-back as a primitive. revoke() tears down the free screens and zeroes the award. Delete your post and the reward evaporates, which means the honest path and the only path are the same path.
Reward systems are adversarial systems

The moment a mechanism hands out something of value, you should assume someone will try to trigger it twice, race it, or undo the proof after collecting. Deterministic IDs, ordered writes, and a claw-back primitive aren’t gold-plating here - they’re the difference between a reward loop and a leak.

Protecting the People Who Actually Pay

Free reach is worthless if it starves the customers funding the platform. So amplified posts live in a separate, deprioritised inventory tier. On a screen that’s already sold, free content is capped at a small share and can never crowd out paying placements below a floor. On unsold screens it fills the gap entirely. Paying customers are insulated from the generosity by design, not by hoping the numbers stay small.

What Actually Changed

The code delta between version one and version two isn’t huge. The mental delta is the whole thing:

  • Don’t reward a metric you can’t verify. If you can’t check it, you’re rewarding the fake version of it.
  • Friction that produces proof beats frictionless gaming. A human-approval step looks like a cost. It’s the product.
  • Make skin in the game the entry fee. When the reward multiplies real investment, there’s no zero-cost abuse path to defend.
  • Treat the reward path as adversarial. Idempotency and claw-back are load-bearing, not polish.

Likes were easy to build and impossible to trust. Proof-of-post is more work for me and more work for the user, and that extra work is precisely what makes the reward mean something. You can’t bot a billboard. As it turns out, you shouldn’t let anyone bot the path to a free one either.