import api_response produces the JSON envelope strings public API
handlers return. Every helper writes a stable shape with ok, optional
data fields, and request_id, so clients and audit logs can correlate on
the same key.
The ManualResponseEnvelopeRule (VCH010) flags hand-built envelopes;
this module is the single place handlers build responses from.
import api_response
return api_response.ok_object("issue", issue_to_json(created))
return api_response.ok_array("issues", json_array_map(rows, issue_to_json))
return api_response.ok_fields(extra_fields)
| Helper | Shape |
|---|---|
ok_object(name, value) | { "ok": true, <name>: { ... } } |
ok_array(name, value) | { "ok": true, <name>: [ ... ] } |
ok_fields(fields) | { "ok": true, ...fields } (spreads a JSON object) |
return api_response.ok_page(
Json.empty_object(),
"issues",
issue_array,
limit,
next_page_token)
ok_page(fields, name, value, limit, next_page_token) returns
{ "ok": true, ...fields, "limit": limit, "next_page_token": <int|null>, <name>: [...] }.
Pair it with paging.page_slice to compute the slice and cursor from a
typed query that fetched limit + 1 rows.
| Failure | Helper | Notes |
|---|---|---|
| Single field failed validation | validation_error(field, message, rid) | Tags field_errors with path for client highlighting. |
| Multiple fields or shape-level failure | error_fields(code, message, field_errors, extra, rid) | Decoder uses this when more than one field is wrong. |
| Caller lacks a capability | capability_error(message, capability, rid) | Names the capability in extra for scope-aware retry. |
| Anything else | error_object(code, message, rid) | Default for not-found, conflict, or generic refusal. |
rid should be ctx.request_id.value; when no id is available the helper
defaults to "req_unavailable" instead of silently dropping the
correlation.
return api_response.validation_error("title", "must be 1-200 chars", ctx.request_id.value)
return api_response.capability_error("forbidden", "issues.moderate", ctx.request_id.value)
return api_response.error_object("not_found", "issue not found", ctx.request_id.value)
For routes that declare a response shape, prefer returning typed values
through the contract instead of building envelopes directly. Use this
module when the route's contract intentionally returns a raw string
envelope (mutations with custom shapes, error paths, or pagination wrappers
that pre-date the response DSL).