UAT·OPS Open the app

Multi-tenant API integration workbench

Nothing here
is mocked.

UAT-Ops onboards customers onto real APIs. Every request in this trace actually left the building, carried credentials injected server-side, and landed in an audit log. The one that stopped, stopped because policy said so.

run · flow_payment_onboarding tenant_acme
  1. POST /oauth/token 200 142ms
  2. GET /v1/accounts 200 89ms
  3. POST /v1/payments 201 310ms
  4. GET /v1/payments/{id} 200 76ms
  5. DEL /v1/payments/{id} HALT

assertAutonomyAllowed() denied · operation class destructive is not delegated for this tenant. Waiting for an operator.

The constraint

One rule made the rest of the system necessary.

There are no local API implementations here. Every provider is somebody else's live sandbox, so a stubbed response would be a lie about whether the integration works — which is the only question the tool exists to answer.

Holding that line has consequences. Real calls need real credentials, so credentials are decrypted and injected at the server boundary and never reach the browser. Real calls produce real evidence, so every execution is logged and failures open issues on their own. And once the assistant is allowed to make those calls unattended, "be careful" stops being a strategy — it needs a gate that the build itself can prove you went through.

The workbench

Four surfaces, one loop:
ingest, compose, run, keep the receipts.

The API Endpoints screen, listing thirteen imported endpoints with their methods, paths, descriptions and test coverage.
Ingest

Point it at a spec, get a working surface.

Drop in OpenAPI, a Postman collection, an Insomnia export, or plain markdown. The format is detected rather than declared. Parsing and committing are deliberately two steps, so you review the diff before anything is written.

The Flow Builder, showing a four-step order placement flow with the selected step's endpoint and generated request preview.
Compose

Chain the calls. Pass the values forward.

Steps run in order, and each one can pull {{variables}} out of any earlier response. When a step fails you decide whether the run stops or carries on without it — per step, not per flow.

The execution log, showing thirty-six runs with status codes and durations, and one execution opened to reveal its recorded request and response headers.
Run

Every call, kept.

Requests, responses, timings and correlation IDs are recorded as they happen. A 4xx or 5xx opens an issue without anyone filing it. Raw bodies age out on a retention clock; the trail they leave behind does not.

The Copilot workspace, scoped to a single tenant and provider, offering actions it is permitted to take.
Delegate

An assistant that can act — inside an envelope.

Autonomy is off until a tenant admin turns it on, and what gets turned on is specific: which operation classes, which providers, what rate and budget, which hours. Anything outside that, the assistant asks instead of assuming. Pause takes effect at the next call, not the next deploy.

How it holds

Three boundaries, each enforced
somewhere you can't forget to check.

A schema per tenant, not a column.

Every tenant — including each reseller's own customers — is provisioned into a dedicated Postgres schema. Isolation sits at the schema boundary, so forgetting a WHERE clause cannot spill one tenant into another. Routes resolve their database handle through a helper that throws rather than quietly falling back to the shared schema.

schema tenant_<slug>

One door, proven by the build.

All autonomous execution funnels through a single gate. That isn't a convention people remember — a guard walks the require graph at build time and fails if any path from autonomous code reaches the executor without passing through it. Authority lives in the tenant's own schema too: a global policy file would let one admin change another tenant's limits.

gate assertAutonomyAllowed()

The server makes the call. Always.

The browser never talks to a provider. It posts an intent, and the server builds the request, decrypts and injects the right credentials, follows redirects under SSRF checks, and returns what actually came back. Token endpoints are the interesting exception — inject a stale bearer into /oauth/token and the provider rejects it, so those are marked to receive nothing.

route POST /api/execute

Scale

What it took.

Application code
164,228lines
Test code
53,672lines
Test files
324
Services
101
Route modules
76
Migrations
90

Tests are mutation-checked rather than trusted: a tool flips one token at a time in the source and reports anything the suite failed to notice. It exists because a vacuous test — one that passes whatever the code does — reviews exactly like a good one.