The response schema already tells you what screen to draw
Half your endpoints can produce a structured answer and half can only produce a sentence. Which half is which is already written down, in the part of the spec nobody reads.

The demo works. The assistant calls getInvoices, gets an array back, and answers in a sentence: "Your July invoice was $84.20, higher than June." Correct, and useless. The customer asked why, and the honest answer is a comparison with four rows.
To generate UI from an OpenAPI response schema you have to stop treating the spec as a list of things the model can call, and start reading the half of it that says what the model gets back. The instinct after a demo like that is to go back to the prompt. It is almost always the wrong place to look.
That second half is a capability inventory. It tells you which of your endpoints can answer with a screen and which can only ever answer with a paragraph — before anyone writes a prompt, and without running anything. Your OpenAPI spec is already the integration; the response section is the part that decides what the integration can show.
The short version
- Read the
200response schema for each allowlisted operation, not just its parameters. - Classify the shape: scalar, flat object, array of like objects, array of unlike objects.
- Look up what your target record type requires — its mandatory fields and its cardinality bounds.
- Match the two. An operation can produce a given screen element only if its response has the arity and the field types that element requires.
- Everything that fails the match is a narrative answer. That is fine — it is just a thing you now know in advance rather than discover in a demo.
Why reading only the request half breaks
Every library in this space consumes the request side of the document and discards the rest. That is not sloppiness; for a chatbot it is correct. A chatbot stringifies whatever comes back and narrates it, so the response schema genuinely is an implementation detail.
You can watch the assumption in the documentation of the two most-cited implementations.
Vercel Labs' json-render ships an OpenAPI integration whose entire worked example converts a CreateUserRequest schema into form fields — strings to inputs, enums to dropdowns, booleans to toggles. Response rendering is named as a benefit in one line and given no mapping. Checked 21 August 2026.
Google's Agent Development Kit is explicit. Its OpenAPI toolset "dynamically creates a FunctionDeclaration based on the operation's parameters and request body." Response schemas are parsed and then play no part in what the agent can do with the result. Checked 21 August 2026.
Search the query itself and the results get further away rather than closer: Swagger UI, openapi-ui, Quarkus, Azure Data API Builder. All of them generate a browsable view of the spec. That is a UI of your API. The thing you need is a UI from an API response, and it is a different problem with a different input.
Change the output from a paragraph to a screen and the responses section stops being documentation and starts being a contract you have to plan against.How to generate UI from an OpenAPI response schema
Three steps, none of which touch your backend, and all of which you can do with the file open in an editor.
Read the success response, not just the parameters
For each operation on your allowlist, walk to responses → 200 → content → application/json → schema, resolving $refs as you go. That object is the whole input to this process. If the operation documents a 201 or 202 instead, use that.
The output is a list of operation ids paired with a schema. It is worth writing down even before you decide anything, because the next two steps are mechanical and this one is the one that turns up surprises.
Classify the shape
Four buckets cover almost everything a REST API returns:
A scalar or a one-field object. A balance, a status string, a count. One value.
A flat object with several named fields. An account, a policy, an order. A set of labelled values, all describing one subject.
An array of objects that share a key set. Invoices, plans, flights, claims. Several subjects described the same way — the richest shape available, and the one most likely to be under-used.
An array of objects that do not share a key set, or an array whose items schema is a oneOf. Mixed content. Harder, and often a sign that the endpoint is doing two jobs.
Check arity against what the record requires
Every structured element has requirements, and they are not vibes — they are minimum and maximum item counts and mandatory fields. A comparison needs at least two subjects to compare. A metric row needs at least two metrics or it is a sentence with a border around it. If the schema cannot supply them, the element is unavailable no matter how the question is phrased.
The four shapes, and what each one can become
A scalar can become a metric, and nothing else. One number with a label and a unit. It cannot become a comparison, because there is nothing to compare it to; if you want the comparison, you need a second call or a different endpoint. Most "what is my balance" endpoints live here, and most teams over-estimate them.
A flat object becomes a facts block. Label-and-value pairs, one subject. Order date, carrier, tracking number, status. This is the workhorse shape and it is usually the one an API is best at, because a GET /resource/{id} that returns a well-documented object is the endpoint everyone gets right.
An array of like objects is the valuable one. It can become a collection, a comparison, a metric row, or a set of steps, depending on the field types inside it. Two or three items with a shared key set and mostly scalar values is a comparison. Several items each with a title and a subtitle is a collection. Items with a sequence field are steps. This is the shape that produces the answers text cannot: "here is July next to June, by line item."
An array of unlike objects usually becomes a collection or nothing. Without a shared key set there are no rows to align, so the comparison is off the table. If this describes your most important endpoint, splitting it is a better investment than prompt work.
Notice what is doing the deciding in each case: the arity of the response and the types of its fields. Not the endpoint's name, not its description, not how important the product team thinks it is.
Cardinality is the part everyone gets wrong
The mapping above sounds like a heuristic. In a system that validates its output it is not — the bounds are literal, they live in the schema, and they reject at runtime.
Here are two records from the wire contract Uzori's model emits, quoted from ndjson-record.schema.json:
{
"comparisonRow": {
"type": "object",
"additionalProperties": false,
"required": ["type", "label", "values"],
"properties": {
"type": { "const": "comparison_row" },
"label": { "type": "string", "minLength": 1, "maxLength": 80 },
"values": {
"type": "array", "minItems": 2, "maxItems": 3,
"items": { "type": "string", "minLength": 1, "maxLength": 160 }
}
}
}
}minItems: 2, maxItems: 3. A comparison holds two or three subjects — not one, and not seven. So an endpoint that returns a single plan cannot produce a comparison, and an endpoint that returns forty listings cannot produce one either without a preceding decision about which two or three matter. The metrics record is bounded the same way, at two to four items.
Two consequences follow, and both are useful before you build anything.
Your paging defaults are a UI decision. An endpoint whose default page size is 50 will hand back 50 items for a question whose answer is a comparison of three. Something has to narrow that, and it is better to decide where than to discover it.
"Required" in the response schema is doing real work. comparison_row requires both a label and its values; a facts entry requires a label and a value. A response whose fields are all optional cannot guarantee either, which means it can produce that element sometimes and not others — the worst of the three outcomes, because it passes your demo.
What this still does not protect you from
A spec that is not true. Every step above reads the documented schema, and if the documented schema and the live response disagree, you have planned a screen against a shape that will not arrive. This is the hard prerequisite for the whole approach and it deserves its own treatment; it is the first thing to check and the most common reason a team is not ready.
A response schema of {"type": "object"} with no properties. Legal OpenAPI, common in generated specs, and completely uninformative. There is nothing to classify. The work here is documenting the endpoint, and no amount of runtime cleverness substitutes for it.
Fields that are prose. A description field containing three sentences of marketing copy has a string type and no structure, so a mechanical read counts it as usable when it is not. Scan for long free-text fields by hand.
Judgement about what matters. The classification is mechanical; deciding which of your operations answer a question a customer actually asks is not, and it is an afternoon of someone senior's time rather than a script.
How Uzori does it
The response schema is read at spec-parse time and travels with the tool, rather than being discarded after the arguments are built. In openapi-provider.ts, each allowlisted operation resolves its success response — responses["200"], falling back to "201" and "202" — down to content["application/json"].schema, and that becomes the tool's outputSchema alongside the inputSchema built from its parameters. Both halves of the operation survive into the manifest.
What the model may then emit is a fixed contract of 23 record types. The constraint matters more than the catalogue: no colour, font, spacing, or tree field exists anywhere in it, and every definition is closed with additionalProperties: false. The model chooses which record fits the data it has. It cannot invent a record, cannot exceed a record's item bounds, and cannot restyle one — so the mapping from response shape to screen element is not advisory. A record that violates its own cardinality is rejected at the gate rather than rendered.
That is what makes the capability inventory worth building. In a system where the model can emit arbitrary markup, an over-optimistic guess about an endpoint degrades into an odd-looking screen. In one where the contract is closed, it is a rejected record, so knowing in advance which endpoints can supply which shapes is the difference between designing the answer and discovering it.
Frequently asked questions
Can you generate a UI from an OpenAPI spec?
Two different things go by that name. Tools like Swagger UI generate a browsable view of the spec, and tools like json-render generate forms from request-body schemas. Generating the UI that displays an API response is a third thing, much less covered, and it starts from the responses section rather than the parameters.
Does the model see the response schema?
In most agent frameworks, no. Google's ADK builds its function declaration from parameters and request body only, and that is typical. Whether it should depends on the output: a chatbot narrates whatever arrives and does not need it, while anything that renders structure has to plan against the shape before the call returns.
What if my response schema is just "object" with no properties?
Then nothing downstream can plan against it, and that endpoint can only produce a narrative answer. This is common in specs generated from code without response annotations. Documenting the two or three operations that answer your most frequent customer questions is a smaller job than it sounds and unblocks everything else.
Which of my endpoints make good structured answers?
The ones returning arrays of objects that share a key set, with mostly scalar fields and a stable set of required keys. Those can become comparisons, collections, or metric rows. Single-value endpoints give you one metric, and endpoints returning free text give you a paragraph regardless of what you do at the prompt layer.
Where to start
Open your spec and spend twenty minutes on three questions.
- For your ten most-contacted-about operations, what does the
200schema actually say? Write down the shape of each. Some will be missing entirely, and that absence is a finding. - Which of them return arrays of like objects? That set is your structured-answer budget. It is usually smaller than expected and it is the honest ceiling on what an assistant can show.
- What are the required fields in those arrays? Anything optional will be present in your test data and missing in production, and it decides whether a shape is reliable or occasional.
If the answer is that a handful of your endpoints are genuinely rich and the rest are sentences, that is the normal result and it is enough to build on. We would rather show you the record contract than describe it — the wire schema and the integration format are two files, and reading them takes about as long as reading this page. Email hello@uzori.ai if you want them against your own spec.