Skip to content

Why AAT Exists

AAT started as a pile of Postman collections.

They worked, at first. Then the team grew. Everyone had their own copy with their own tweaks, and none of them were reliable. Nothing was in source control, so there was no diff, no review, and no way to tell whose version was right. Every new test case meant editing a collection in place, so the case it replaced was gone. The chaining lived in pre-request scripts, which put the interesting part of a flow — what depends on what — inside JavaScript instead of in front of you. And none of it was legible to an AI coding tool: the export was one file too large to read, in a shape nothing else consumes.

The underlying problem is that real integrations are not one call. Buying something means browse, cart, checkout, pay, ship, and maybe return and refund: 8 to 20 calls, each needing IDs from the calls before it, leaving state behind that someone has to clean up. A collection is a folder of single requests. Everything that makes those requests a flow has to live somewhere else, and that somewhere was scripts.

Postman is good at what it is for: exploring an API by hand, one request at a time. It is a poor place to keep the knowledge of how an API works. That knowledge ends up in a format only Postman reads, in a workspace rather than your repository, and it scales by copying.

So the knowledge moved into the repository. API knowledge is a graph of operations and request templates, written once. Test intent is a plan that lists steps, not wiring. Variation is layers and environments that turn one plan into a matrix. Small files, reviewed like code, that an AI coding tool can read one at a time and a person can follow without opening a debugger.

The point is not that the files are tidy. It is that they run.

What changed, item by item

What went wrong with a pile of collections What AAT does instead
Everyone had a copy, with their own tweaks One graph in the repository. Plans name steps, not wiring, so fixing an operation fixes every test that uses it
None of them were reliable The project runs. aat validate --strict catches broken wiring before a request is sent, --oas-validate checks every exchange against the spec, and the archive keeps the request and the response
Every new case meant mutating the collection Layers turn one plan into a matrix, and nothing is edited in place
No source control One small YAML file per operation, reviewed in a pull request and diffed like code
An AI tool could not use it A 21 MB export does not fit in a context window; a 23-line template does. The MCP server hands an assistant the flows as tools rather than prose
The scripting hid the lede Wiring is declared, not scripted. The archive shows every value, where it came from, and every decision

The two numbers in that last row are measured, not rhetorical. The private airline API this tool was built against ships a 40,000-line OpenAPI spec, and the team's Postman collections for it were 21 MB and 23 MB. The AAT project that replaced them is a 3,708-line graph covering 74 operations, with a median request template of 23 lines. An assistant — or a reviewer — loads the one operation the task needs, and edits one file.

What else could read it

Once the API was described well enough for the engine to run it — typed operations, where each value comes from, what has to happen first, what undoes what — the description turned out to be worth more than the tests it was written for. The question stopped being what else should this run? and became what else can read this?

AI coding tools. aat mcp serve hands an assistant the same graph the engine runs: each operation's exact request, the order calls go in, what each one needs from the calls before it, the composed flows, and sample responses from real runs. It is a form a machine can act on, rather than prose it has to interpret. Package a subset as an integration kit and your integrators' assistants read it too — on a 74-node airline API, that was enough for a working client in a single prompt, in six languages. LLMs are optional and authoring-time only: aat prompt can draft a plan, and the MCP server teaches AI tools your API. Execution never calls an LLM.

Evidence you can send someone. Every run writes an archive: each request and response, how every input got its value, every retry, every assertion, and the cleanup, with secrets redacted. The web UI exports a run as a single .aar file, and whoever you send it to opens it in the same viewer with aat web view or aat import. It is how you show that something works — or that it doesn't — with the actual exchange instead of a screenshot of one pane, and it is the difference between "the sandbox rejects this" and a file the other team can open and step through.

The shop's checkout step in the web UI: node, status, and display outputs, the Request tab with the method and URL, the Copy as cURL button, the headers with Authorization redacted, and the JSON body

Reference documentation. aat docs generate writes Markdown for every operation from the graph, so the description that runs the tests is also the page someone reads.

Whatever comes next in your pipeline. --stop-after stops a run at a named step and leaves the resources it created alive; --dump-state writes their IDs, base URLs, and headers for a pytest suite, a load test, or a curl session to pick up. For CI there are exit codes 0/1/2/130, --json, --quiet, JUnit XML, and a Docker image.

None of that was a roadmap. It is what one good description of an API turned out to be good for, and it is what the toolkit in the name means. Adaptive is the other half: the many ways one graph runs — layers, environments, per-operation overrides, checkpoints, batch matrices, the MCP server — without a copy of anything.

It is legible both ways

The same property that makes the files fit an agent's context window makes them fit a reviewer's head: one operation per file, one plan per scenario, no hidden scripting between them. When you want detail rather than summary, the web UI turns a run into a timeline of every step, with the resolved value behind every input, every retry and assertion, and Copy-as-cURL on any step.

The proof is that it runs

Four complete projects against real, public APIs, each built openly: three run against an API's live test mode, and the fourth against the database itself, in a local container. Every claim in their READMEs is something a run recorded.

Project Scale What it shows
aat-duffel 66 operations, 47 plans, 14 layers; the full batch passes 47/47 in about 3½ minutes An API with no official OpenAPI spec. Everything the README says about Duffel came from runs
aat-stripe 82 operations, 53 plans, 14 layers; 53/53 in about 5 minutes About 6,300 lines of graph and templates against Stripe's 205,000-line vendored spec, with every request and response checked against it as it goes
aat-shippo 46 of 70 operations, 28 plans, 9 layers; 28/28 in about 2½ minutes Layers as the headline — a lane × parcel matrix and six deterministic tracking fixtures — with real shipping labels rendered in the web UI
aat-qdrant all 52 public unary gRPC methods as 77 operations, 38 plans, 6 layers; 38/38 in about 70 seconds The same files over gRPC: about 5,400 lines of YAML against a 4,700-line proto surface nobody on the AAT side wrote, every node checked offline against Qdrant's published protos, and every refusal asserted by status name and exact message

aat-shippo makes the argument on this page in one command. Its lane × parcel matrix runs eight combinations from two plan files. A second matrix, over an axis those plans never read, expands to fourteen runs — seven execute, seven are skipped as duplicates, ten seconds, nothing bought. A layer only multiplies the plans it actually reaches. With collections, every one of those combinations is a copy you maintain by hand.

aat-qdrant makes a different one: the argument does not depend on the protocol. The graph, the plans, the layers, and the archives are the same files over gRPC as over REST. It was built to find what AAT's gRPC support had not thought of, and twelve changes to AAT came out of building it, each made as the gap turned up.

Real APIs says what each covers, proves, and leaves out. Three smaller projects ship in this repository and need no account at all: the shop and gRPC payments, which run offline against aat-sandbox, and the petstore. See Examples.

If you already have API tooling

  • An OpenAPI spec is the best starting point. aat generate --oas scaffolds the graph and one template per operation — roughly the mechanical 70% — and you add the part a spec cannot describe: which calls reach a goal, in what order, and what undoes what.
  • A Postman collection has no importer, and this page will not pretend otherwise. What works today is to point your AI coding assistant at the collection through a reader such as expost and have it author the graph, then close the loop with aat validate --strict and a real run. The AI assistant primer covers that workflow.
  • Nothing yet is fine too. The Tutorial builds a project by hand against the offline sandbox in about 45 minutes.

I built AAT because working with someone else's API should cost less than working around it.