REST API maturity is often evaluated using the Richardson Maturity Model (RMM), which defines four levels:
- Level 0 – Single URI, typically using POST for everything
- Level 1 – Resources identified by URIs
- Level 2 – Use of HTTP methods to define behavior (GET, POST, PUT, DELETE)
- Level 3 – Hypermedia controls (HATEOAS)
However, traditional REST maturity models often overlook an important aspect: schema awareness. That’s why I would like to propose a new, unofficial but pragmatic step after Level 3:
Level 3.1: JSON Schema-Aware Hypermedia
This level builds directly on HATEOAS by adding machine-readable JSON Schema definitions directly into your API responses and hypermedia links. This allows clients not only to discover available actions (via HATEOAS) but also understand exactly what data to send.
The Anatomy of a Level 3.1 API
Response Example
{
"$schema": "https://api.example.com/schemas/user.json",
"id": "123",
"name": "Alice",
"_links": {
"self": { "href": "/users/123" },
"update": {
"href": "/users/123",
"method": "PUT",
"body": {
"$ref": "https://api.example.com/schemas/user-update.json"
}
}
}
}Key Points:
$schemadefines URL to the resource’s JSON Schema.bodyin a link contains the expected JSON Schema for the request body — either inline or via$ref.
Example with Inline Schema:
{
"$schema": "https://api.example.com/schemas/user.json",
"id": "123",
"name": "Alice",
"_links": {
"self": { "href": "/users/123" },
"update": {
"href": "/users/123",
"method": "PUT",
"body": {
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string", "format": "email" }
}
}
}
}
}Why Add a 3.1 Level?
- Self-validating
Clients can validate requests before sending them. There is no need to duplicate validation rules between front-end and back-end applications. - Self-documenting
No need for out-of-band docs — clients discover schemas in real time. Pacakges preparing the JSON Schema can also offer preparing OpenAPI specs based on the schema. - UI-friendly
Dynamic form generators can use schema to render inputs. There is plenty of libraries which can render a form based on JSON Schema, sometimes optional UI metadata can be added for UX. - Consistent
Ties in naturally with JSON Schema and OpenAPI tooling.
Caching and Versioning of Schemas
To ensure performance and long-term consistency, clients should cache any JSON Schemas once they are retrieved. Since schemas are often reusable and may not change frequently, caching avoids redundant network requests and speeds up validation and UI rendering. APIs should add Cache-Control headers ensuring that browsers will cache the schemas.
To support safe evolution over time, schemas must be versioned. This can be achieved by encoding version numbers in the schema URL (e.g., /schemas/v2/user.json or /schemas/user.json?version=1.2.3). Versioning ensures that cached schemas will be invalidated once a new API version will be available.
Best practices include:
- Use explicit semantic versioning in schema URLs
- Align schema version with API version
- Avoid breaking changes in existing schema versions
Naming Conventions: Staying Idiomatic
Because this proposal extends HAL, naming should follow HAL conventions:
- Use
snake_casefor custom properties (body,body_schema, etc.) - Keep
_linksand_embeddedconsistent
We chose body as the field name because it is short, intuitive, and always holds a JSON Schema compatible object.
Optional Enhancements
In future versions, we will extend the idea with:
- Content types (e.g.,
application/xml) via acontent_typeproperty - Payloads alongside schema
- Embedded UI metadata (like input widgets)
Final Thoughts
By adding JSON Schema awareness to your HATEOAS-powered links, you unlock a smarter, more self-sufficient API ecosystem. This 3.1 maturity level invites a world where clients no longer need hardcoded assumptions.
Your API becomes self-expressive.
TL;DR
- Extend HAL links with a
bodyfield containing a JSON Schema. - Use
$reffor external schemas or inline full definitions. - Prefer HAL-style naming (
snake_case). - Achieve a new level of API self-discoverability and automation.
Welcome to Level 3.1.
