Somewhere around the tenth customer, every B2B SaaS gets the same three requests. A customer asks "who removed that teammate?". Support asks "can you tell me what this user did before the bug?". And a prospect's security review asks "where's your audit trail?".
And every team answers the same way: "we'll add an events table."
The quick table that never stays quick
-- v1: "just a quick events table" CREATE TABLE events ( id uuid PRIMARY KEY, user_id uuid, type text, -- free-form, drifts within a month payload jsonb, -- everything and nothing created_at timestamptz );
It works for the demo. Then reality arrives in stages: the type column drifts because three services name events three different ways. The payload blob means every query needs tribal knowledge. Pagination breaks the first time two events share a timestamp. Retention never gets built, so the table quietly becomes the biggest one in the database. And the customer-facing timeline — the thing that started all this — is still a pile of frontend edge cases: loading, empty, error, "load more".
We know because we built this. More than once, at more than one company. Each version took weeks to ship and kept costing maintenance forever after. None of it differentiated the product — customers don't buy you because your events table is nice. They just expect the history to be there, complete and correct.
What the boring parts actually require
Done properly, "just an events table" turns out to be a small platform:
- A stable event contract — actor, action, target, metadata — enforced at the edge so naming can't drift.
- Ingestion that can't hurt you: non-blocking SDKs that swallow failures, rate limits, batch endpoints for browser traffic.
- Cursor pagination that stays correct while events stream in.
- Retention enforced automatically, per project, so storage and compliance stay predictable.
- A key model where the browser can write but never read.
- The UI states nobody budgets for: loading, empty, error, theming to match your product.
Each item is unglamorous. Together they're 6–12 weeks of engineering — spent on infrastructure your users assume already exists.
So we built it once
Softechlog is that platform, extracted: one track() call from Node or Python, an optional auto-capture script for UI events, a query API with real filtering, and a drop-in <softechlog-feed> component for the customer-facing part. The quickstart is genuinely about ten minutes.
We're dogfooding it on this very site — the beta signups you send us arrive as waitlist.requested events in our own dashboard.
If you're pre-launch or early-stage and staring down your own "quick events table", we'd love to have you in the beta. Build the product instead.