
JSON:API Compliance
FreeEnsure strict adherence to JSON:API v1.1 specifications.
Free · Opens the source repo
What JSON:API Compliance does
The JSON:API Compliance skill is designed to help developers and designers ensure that their API endpoints are compliant with the JSON:API v1.1 specification. This skill is particularly useful when creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance. By adhering to the strict rules set forth in the specification, developers can avoid common pitfalls and ensure that their APIs are robust and reliable.
This skill works in conjunction with the django-drf skill, which provides implementation patterns for Django REST Framework (DRF). While the jsonapi skill focuses on what the specification requires, the django-drf skill provides guidance on how to implement those requirements in code. For best results, developers should invoke both skills when working on API endpoints, ensuring that they not only meet the specification but also follow best practices in their implementation.
Before implementing or reviewing API endpoints, it is crucial to validate against the latest JSON:API specification. The skill offers two options for validation: using Context7 MCP for direct queries to the specification or a fallback method using WebFetch to retrieve the official spec. This ensures compliance with the most current version of JSON:API, even as updates occur.
With a focus on critical rules, the skill outlines essential guidelines for document structure, content types, resource objects, relationships, error objects, and HTTP status codes. By following these rules, developers can avoid common violations and ensure that their APIs behave as expected, providing a clear and consistent experience for clients.
When to use it
Use this skill when creating or modifying API endpoints to ensure compliance with JSON:API standards.
When not to use it
This skill may not be suitable for APIs that do not require JSON:API compliance or when using a different API specification.
What you can build with it
Creating a New API Endpoint
When designing a new API endpoint, use this skill to ensure all responses comply with JSON:API specifications.
Reviewing API Responses
Utilize this skill to validate existing API responses against the JSON:API standard during code reviews.
Modifying Existing Endpoints
Before modifying any API endpoints, invoke this skill to check for compliance with the latest JSON:API rules.
How to install JSON:API Compliance
View source1. Install with the skills CLI
npx skills add prowler-cloud/prowler/jsonapi --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by prowler-cloudUse With django-drf
This skill focuses on spec compliance. For implementation patterns (ViewSets, Serializers, Filters), use django-drf skill together with this one.
| Skill | Focus |
|---|---|
jsonapi | What the spec requires (MUST/MUST NOT rules) |
django-drf | How to implement it in DRF (code patterns) |
When creating/modifying endpoints, invoke BOTH skills.
Before Implementing/Reviewing
ALWAYS validate against the latest spec before creating or modifying endpoints:
Option 1: Context7 MCP (Preferred)
If Context7 MCP is available, query the JSON:API spec directly:
mcp_context7_resolve-library-id(query="jsonapi specification")
mcp_context7_query-docs(libraryId="<resolved-id>", query="[specific topic: relationships, errors, etc.]")
Option 2: WebFetch (Fallback)
If Context7 is not available, fetch from the official spec:
WebFetch(url="https://jsonapi.org/format/", prompt="Extract rules for [specific topic]")
This ensures compliance with the latest JSON:API version, even after spec updates.
Critical Rules (NEVER Break)
Document Structure
- NEVER include both
dataanderrorsin the same response - ALWAYS include at least one of:
data,errors,meta - ALWAYS use
typeandid(string) in resource objects - NEVER include
idwhen creating resources (server generates it)
Content-Type
- ALWAYS use
Content-Type: application/vnd.api+json - ALWAYS use
Accept: application/vnd.api+json - NEVER add parameters to media type without
ext/profile
Resource Objects
- ALWAYS use string for
id(even if UUID) - ALWAYS use lowercase kebab-case for
type - NEVER put
idortypeinsideattributes - NEVER include foreign keys in
attributes- userelationships
Relationships
- ALWAYS include at least one of:
links,data, ormeta - ALWAYS use resource linkage format:
{"type": "...", "id": "..."} - NEVER use raw IDs in relationships - always use linkage objects
Error Objects
- ALWAYS return errors as array:
{"errors": [...]} - ALWAYS include
statusas string (e.g.,"400", not400) - ALWAYS include
source.pointerfor field-specific errors
HTTP Status Codes (Mandatory)
| Operation | Success | Async | Conflict | Not Found | Forbidden | Bad Request |
|---|---|---|---|---|---|---|
| GET | 200 | - | - | 404 | 403 | 400 |
| POST | 201 | 202 | 409 | 404 | 403 | 400 |
| PATCH | 200 | 202 | 409 | 404 | 403 | 400 |
| DELETE | 200/204 | 202 | - | 404 | 403 | - |
When to Use Each
| Code | Use When |
|---|---|
200 OK | Successful GET, PATCH with response body, DELETE with response |
201 Created | POST created resource (MUST include Location header) |
202 Accepted | Async operation started (return task reference) |
204 No Content | Successful DELETE, PATCH with no response body |
400 Bad Request | Invalid query params, malformed request, unknown fields |
403 Forbidden | Authentication ok but no permission, client-generated ID rejected |
404 Not Found | Resource doesn't exist OR RLS hides it (never reveal which) |
409 Conflict | Duplicate ID, type mismatch, relationship conflict |
415 Unsupported | Wrong Content-Type header |
Document Structure
Success Response (Single)
{
"data": {
"type": "providers",
"id": "550e8400-e29b-41d4-a716-446655440000",
"attributes": {
"alias": "Production",
"connected": true
},
"relationships": {
"tenant": {
"data": {"type": "tenants", "id": "..."}
}
},
"links": {
"self": "/api/v1/providers/550e8400-..."
}
},
"links": {
"self": "/api/v1/providers/550e8400-..."
}
}
Success Response (List)
{
"data": [
{"type": "providers", "id": "...", "attributes": {...}},
{"type": "providers", "id": "...", "attributes": {...}}
],
"links": {
"self": "/api/v1/providers?page[number]=1",
"first": "/api/v1/providers?page[number]=1",
"last": "/api/v1/providers?page[number]=5",
"prev": null,
"next": "/api/v1/providers?page[number]=2"
},
"meta": {
"pagination": {"count": 100, "pages": 5}
}
}
Error Response
{
"errors": [
{
"status": "400",
"code": "invalid",
"title": "Invalid attribute",
"detail": "UID must be 12 digits for AWS accounts",
"source": {"pointer": "/data/attributes/uid"}
}
]
}
Query Parameters
| Family | Format | Example |
|---|---|---|
page | page[number], page[size] | ?page[number]=2&page[size]=25 |
filter | filter[field], filter[field__op] | ?filter[status]=FAIL |
sort | Comma-separated, - for desc | ?sort=-inserted_at,name |
fields | fields[type] | ?fields[providers]=id,alias |
include | Comma-separated paths | ?include=provider,scan.task |
Rules
- MUST return
400for unsupported query parameters - MUST return
400for unsupportedincludepaths - MUST return
400for unsupportedsortfields - MUST NOT include extra fields when
fields[type]is specified
Common Violations (AVOID)
| Violation | Wrong | Correct |
|---|---|---|
| ID as integer | "id": 123 | "id": "123" |
| Type as camelCase | "type": "providerGroup" | "type": "provider-groups" |
| FK in attributes | "tenant_id": "..." | "relationships": {"tenant": {...}} |
| Errors not array | {"error": "..."} | {"errors": [{"detail": "..."}]} |
| Status as number | "status": 400 | "status": "400" |
| Data + errors | {"data": ..., "errors": ...} | Only one or the other |
| Missing pointer | {"detail": "Invalid"} | {"detail": "...", "source": {"pointer": "..."}} |
Relationship Updates
To-One Relationship
PATCH /api/v1/providers/123/relationships/tenant
Content-Type: application/vnd.api+json
{"data": {"type": "tenants", "id": "456"}}
To clear: {"data": null}
To-Many Relationship
| Operation | Method | Body |
|---|---|---|
| Replace all | PATCH | {"data": [{...}, {...}]} |
| Add members | POST | {"data": [{...}]} |
| Remove members | DELETE | {"data": [{...}]} |
Compound Documents (include)
When using ?include=provider:
{
"data": {
"type": "scans",
"id": "...",
"relationships": {
"provider": {
"data": {"type": "providers", "id": "prov-123"}
}
}
},
"included": [
{
"type": "providers",
"id": "prov-123",
"attributes": {"alias": "Production"}
}
]
}
Rules
- Every included resource MUST be reachable via relationship chain from primary data
- MUST NOT include orphan resources
- MUST NOT duplicate resources (same type+id)
Spec Reference
- Full Specification: https://jsonapi.org/format/
- Implementation: Use
django-drfskill for DRF-specific patterns - Testing: Use
prowler-test-apiskill for test patterns
Frequently asked questions about JSON:API Compliance
Similar skills
WinMD API Search
Easily find and explore Windows desktop APIs.
WebMCPify
Transform any web app into an agent-ready platform.
Phoenix Tracing
Instrument LLM applications with OpenInference tracing.
Foundry Hosted Agent CopilotKit
Guidance for developing agentic web apps on Azure.
Power Automate Foundation
Connect AI agents to Power Automate seamlessly.
Power Automate Flow Builder
Efficiently build and deploy Power Automate flows programmatically.
