From harness-claude
Guides API design with HATEOAS (Level 3 REST), covering HAL and JSON:API link formats for state-dependent workflows like order lifecycle or document approvals.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:api-hateoasThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Hypermedia As The Engine Of Application State (HATEOAS) embeds links to available next actions in every API response. Clients navigate the API by following links rather than constructing URLs — making the API self-describing and decoupling clients from URL structure.
Hypermedia As The Engine Of Application State (HATEOAS) embeds links to available next actions in every API response. Clients navigate the API by following links rather than constructing URLs — making the API self-describing and decoupling clients from URL structure.
"authorId": 42 is different from returning "author": { "href": "/users/42" }HATEOAS is the constraint that distinguishes Level 3 from Level 2 in the Richardson Maturity Model (see api-rest-maturity-model). A HATEOAS response tells the client what it can do next, not just what the current state is.
Without HATEOAS (Level 2):
{
"id": 42,
"status": "pending",
"amount": 150.0
}
The client knows the order is pending, but must have out-of-band knowledge that it can call DELETE /orders/42 to cancel or POST /orders/42/payments to pay. If those URLs change, clients break silently.
With HATEOAS (Level 3):
{
"id": 42,
"status": "pending",
"amount": 150.0,
"_links": {
"self": { "href": "/orders/42" },
"cancel": { "href": "/orders/42", "method": "DELETE" },
"pay": { "href": "/orders/42/payments", "method": "POST" },
"customer": { "href": "/customers/7" }
}
}
The server controls what actions are available. When the order is paid, the pay link disappears and a refund link appears. The client does not need to know that paid orders cannot be paid again — the server stops advertising the action.
HAL (Hypertext Application Language):
HAL is the most widely adopted hypermedia format. It uses _links for navigation and _embedded for inline related resources.
{
"id": 42,
"status": "shipped",
"total": 89.99,
"_links": {
"self": { "href": "/orders/42" },
"customer": { "href": "/customers/7" },
"track": { "href": "/shipments/s99" }
},
"_embedded": {
"items": [
{
"sku": "ABC-123",
"quantity": 2,
"_links": { "self": { "href": "/products/ABC-123" } }
}
]
}
}
JSON:API link format:
{
"data": {
"type": "orders",
"id": "42",
"attributes": { "status": "shipped", "total": 89.99 },
"links": { "self": "/orders/42" },
"relationships": {
"customer": {
"links": { "related": "/customers/7" }
}
}
}
}
A document approval workflow has states: draft, submitted, under_review, approved, rejected. Valid transitions depend on current state and the caller's role.
Without HATEOAS:
Clients must embed a state machine: "if status is submitted and I am a reviewer, I can call POST /documents/d1/approvals or POST /documents/d1/rejections." This logic must be duplicated in every client. When the workflow changes, every client must be updated.
With HATEOAS:
GET /documents/d1
→ 200 OK
{
"id": "d1",
"title": "Q4 Report",
"status": "submitted",
"_links": {
"self": { "href": "/documents/d1" },
"approve": { "href": "/documents/d1/approvals", "method": "POST" },
"reject": { "href": "/documents/d1/rejections", "method": "POST" },
"history": { "href": "/documents/d1/history" }
}
}
A reviewer sees approve and reject links. The document author sees only self and history. The state machine lives on the server; clients follow what is advertised.
Pagination as a practical HATEOAS application:
GET /orders?page=3
{
"items": [...],
"_links": {
"self": { "href": "/orders?page=3" },
"prev": { "href": "/orders?page=2" },
"next": { "href": "/orders?page=4" },
"first": { "href": "/orders?page=1" },
"last": { "href": "/orders?page=11" }
}
}
This is HATEOAS in practice. The client never constructs pagination URLs. See api-pagination-cursor for cursor-based navigation links.
HATEOAS without documented link relations. Links like "action123": { "href": "..." } are meaningless without documentation. Use IANA-registered link relations (self, next, prev, edit, delete) or define custom relations in your API documentation with consistent semantics.
Adding _links to every response regardless of whether it adds value. A self link on a deeply internal resource no client navigates to is noise. Apply HATEOAS where it actually changes client behavior — state machines, pagination, and discovery.
HATEOAS as a substitute for documentation. Links advertise available transitions; they do not describe request bodies, required fields, or error semantics. HATEOAS and OpenAPI are complementary, not alternatives.
Mixing HATEOAS depth inconsistently. Some resources return _links, others return bare IDs. Inconsistency forces clients to handle both patterns. If you adopt HATEOAS, apply it uniformly or not at all.
Hardcoding URLs in the client and ignoring the links. The entire benefit of HATEOAS is that clients follow links rather than construct URLs. If your clients build URLs from templates and ignore the _links, you are paying the response-size cost with none of the decoupling benefit.
Implementing HATEOAS increases:
Cache-Control values.When the cost is worth it:
When to skip it:
Most major APIs implement partial HATEOAS: pagination links universally, state-transition links selectively, full HAL rarely.
Link header for pagination (next, prev, last). Does not use HAL body links.url fields on resources but not HAL _links. Uses expand parameters rather than embedded resources._links including self, approve, capture, void on payment objects. One of the most complete HATEOAS implementations in a major public API.Link headers.The pragmatic conclusion: implement pagination links always, state-transition links for complex workflows, and full HAL only when client-server decoupling over time is a first-class requirement.
status field with constrained valid transitions (order lifecycle, document approval, subscription state)._links to responses that advertise only currently valid transitions for the caller's role. Use IANA-registered relation names where possible.next, prev, first, last) on all collection endpoints — this is the highest-ROI HATEOAS application.harness validate to confirm skill files are well-formed.next, prev, first, and last links in _links or Link headers._links, filtered by caller role.self, next, prev, edit) or are documented custom relations._links and others return bare IDs for the same type of relationship.npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeREST API design with semantic HTTP methods, status codes, and resource modeling. Use when designing new APIs or reviewing existing API designs.
Guides developers through the four levels of REST API maturity, from RPC-over-HTTP to hypermedia controls. Helps evaluate API design, review endpoints, and apply correct HTTP semantics.
Guides API design decisions including REST vs GraphQL, resource modeling, versioning, error contracts, pagination, and authentication patterns.