Skip to content

API ​

Accepted architecture · 1 October 2026, option 2

Agentfy hosts the harness; Store Deputy API exposes the single MCP; the OpenCart module exposes a protected REST/JSON API. Store Deputy governs connections, actor/shopper context, permissions, approvals and audit without building another harness. Connect → login/register → secure pairing → automatic agent provisioning in Agentfy with Store Deputy MCP → return to the module. App contains teams, agents, account, billing and per-agent chat.

This replaces the earlier same-day direct-module-MCP design. Existing code and prototypes below do not implement the new integration. Shopper sessions and the real OpenCart cart have separate scoped bindings; cookies stay in the shop. See the accepted central MCP decision for boundaries, delivery stages and remaining contract work.

Built; runs on a developer's machine · 1 October 2026. Not deployed. It has only ever talked to the two local test shops. It has no catalog, price or agent features.

The API is the one service that knows who a person is, which team they belong to, what their role allows and which stores the team has connected. Every other part asks it. Nothing else talks to a shop.

What it does today ​

  • Accounts. Register, sign in, renew a session, sign out. Signing out takes effect at once: every request is checked against the live session, not only against the token it carries.
  • Teams and roles. A team owns stores. Its members are owners, admins or members. Owners and admins invite people; only an owner changes roles, removes another owner or deletes the team. A team cannot be left without an owner.
  • Stores. Connect a shop with the one-time code from its module, list a team's stores, look at one, disconnect it. The secret shared with the shop is stored encrypted.
  • Service checks. A health check, and an interactive description of every route.
AreaRoutes
Sign-in/auth/register, /auth/login, /auth/refresh, /auth/logout, /auth/me
Own profile/users/me
Teams/teams, /teams/:teamId
Membership/teams/:teamId/members, /teams/:teamId/members/:memberId
Stores/teams/:teamId/stores, /teams/:teamId/stores/connect, /teams/:teamId/stores/:storeId
Health/health

Registering, signing in, renewing a session and the health check are open. Every other route needs a signed-in person.

How the code is arranged ​

All code lives in slices. There are two groups of feature slices and one group of setup slices.

api/src/slices/
  setup/          shared plumbing, no business rules
    prisma/       the database connection
    error/        one format for every failure
    response/     wraps every success as { data, success: true }
    crypto/       encrypts stored connection secrets
    core/         the marker for routes that need no sign-in
    health/       the health check
  user/           who is acting — a namespace folder with no code of its own
    user/         the person
    auth/         sign-in, sessions and the check on every route
    team/         the team that owns stores
    userTeam/     membership and roles
  store/
    store/        a connected shop, and the client that talks to its module

A feature slice has the same shape every time. The store slice, as an example:

store/store/
  store.module.ts         wires each contract to its implementation
  store.controller.ts     presentation: routes, input, output
  store.prisma            this slice's database tables
  dtos/                   presentation: request and response shapes
  domain/                 rules and contracts
    store.service.ts        who may connect, which addresses are acceptable
    store.gateway.ts        contract: saving and loading stores
    bridge.gateway.ts       contract: talking to a shop's module
    store.types.ts
    errors/                 one file per failure
  data/                   implementations
    store.gateway.ts        PostgreSQL through Prisma
    store.mapper.ts         database rows into domain types
    bridge.gateway.ts       turns the module's answers into domain results
    repositories/bridge/    the HTTP client for Bridge Protocol v1

The database is reached directly through Prisma inside a gateway; there is no separate repository class for it. An outside service is different: the shop module is wrapped in a repository with its own types, so the protocol's wire format stays out of the rules.

One request, layer by layer ​

An owner confirms connecting a shop. This is what happens to that request.

  1. Sign-in check. Before any slice runs, a check registered for every route establishes who is calling and that their session is still live.
  2. Controller. Accepts the request, validates its shape and hands it to the service. It holds no rules.
  3. Service — the rules. The caller must be an owner or an admin of the team. The shop must be reachable over HTTPS; plain HTTP is accepted only for a local address in development and tests. The address the owner returns to must belong to the same shop that is being connected.
  4. Bridge contract. The service asks "pair with this shop". The data layer signs the request and sends it. A reply without a valid signature counts as "shop unreachable", even if the shop answered with a success status.
  5. Store contract. The service asks "save this store". The data layer encrypts the secret and writes the row. An earlier connection to the same shop is marked disconnected.
  6. Controller. Returns the store and the address to send the owner back to.

The service never imports the database client or the HTTP client. It knows two contracts and nothing about how they are met.

Rules every slice follows ​

  • Slice folders are singular; routes are plural.
  • A contract is an abstract class in domain/. Its implementation sits in data/, and the slice's module ties the two together.
  • A slice uses another slice through its domain. The store slice asks the membership service whether the caller's role is allowed; it never reads another slice's data layer.
  • Every route carries an operation name. The app's generated client takes its method names from it.
  • Every route requires sign-in unless it is explicitly marked as open.
  • Each slice owns the schema of its own tables. A script assembles them into one schema before the database tools run.
  • Imports between slices use short aliases — #setup/…, #user/…, #store/… — not long relative paths.

The points where this API knowingly differs from CleanSlice are listed in the overview.

How it is checked ​

End-to-end scenarios run against a real PostgreSQL in Docker, not against mocks. The store scenarios also need the two local test shops running. Their last recorded results are in the repository's plan, dated 29 September 2026. They were not re-run for this page.

Not built yet ​

  • Tools for agents, and any link to Agentfy.
  • Reading a shop's catalog.
  • Price proposals, approvals, jobs and the operation ledger described in the implementation proposal.
  • Email confirmation and password reset.
  • A limit on sign-in and registration attempts.
  • Rotation of the key that signs connection requests.
  • Anything for platform operators. Each person has a platform-role field, but nothing grants it and nothing checks it. See Admin.
  • Deployment. No hosting has been chosen.

Module ↔ API protocol: REST 1.0