
One source of truth: how the site builds itself from the catalog
We had our services stored in the database and written out again on the website. Every change had to be made twice, and one side was always stale.
Giving an agent direct database access looks faster. It is, right up until the day a creative agent runs an UPDATE without a WHERE.

When you build an agent that needs data, the shortcut is obvious: hand it a database connection and let it query what it needs. Less code, less latency, fewer moving parts.
We went the other way and wrote it down as a hard rule: the backend is the only door to the database. Agents talk over HTTP, like any other client. No exceptions.
The first is obvious and the least important. An agent with free SQL can write an UPDATE without a WHERE. Yes, you can mitigate that with read-only permissions, but that solves half the problem and none of the next two.
The second is business logic. An order moving to "paid" is not writing a field. It is verifying the payment, generating the receipt, activating the subscription, scheduling the renewal, and notifying through the right channel. If the agent writes the field directly, none of that happens. And the worst part is that it looks like it worked: the record says "paid" and nobody finds out until the client complains.
The third is the one that grew on us the most: traceability. When everything goes through the API, everything lands in a log with who, when, and with what payload. You can reconstruct what an agent did three weeks ago. With direct database access you get a state change with no author.
Agents carry a system key on every call. It is not a user session: it is the agent's own identity, with its own role and its own permissions. The API can tell "a logged-in person asked for this" apart from "the sales agent asked for this," and applies different rules.
One detail turned out to matter more than expected: there are endpoints an agent cannot call even holding the key. Contact personal data, for instance, requires explicit authorization, so that an agent does not become an indirect way to enumerate the client base.
This decision is not free. Every new piece of data an agent needs means an endpoint, its validation, and its test. There are days where three lines of SQL turn into half an hour of work.
We accept it for one reason: most of the serious problems we have had were not an agent doing something spectacularly wrong. They were an agent doing something reasonable in the wrong place, skipping a step it did not know existed. The API is where that knowledge lives.
Something happened over time that we had not anticipated. Because agents consumed the API, the API had to be well designed: clear names, explicit errors, complete responses. An endpoint that returns a bare 500 leaves the agent with no idea what to do, and you notice immediately.
We ended up with an API better documented than it would have been if only our own frontend consumed it. Agents turned out to be very demanding users, and that demand did us good.

We had our services stored in the database and written out again on the website. Every change had to be made twice, and one side was always stale.

An agent that remembers everything is slow, expensive and confused. Designing what gets discarded turned out to be harder than designing what gets kept.

Idempotence, migrations, soft deletes, failing fast. Decades-old concepts that turned critical exactly when the one writing the code is not always human.
Tell us what you need and we'll tell you how we'd approach it. In minutes, not weeks.
No strings attached. The first chat is free and we reply right away.