Skip to content
Security
Skill

/jsonapi

Strict JSON:API v1.1 specification compliance. Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.

From plugin
prowler
15k39 skills1 MCP
Install
$ npx -y skills add prowler-cloud/prowler --skill jsonapi --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/jsonapi

Context preview

The summary Claude sees to decide when to auto-load this skill.

Strict JSON:API v1.1 specification compliance. Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.

SKILL.md

jsonapi.SKILL.md
name: jsonapi
description: >
  Strict JSON:API v1.1 specification compliance.
  Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.
license: Apache-2.0
metadata:
  author: prowler-cloud
  version: "1.0.0"
  scope: [root, api]
  auto_invoke:
    - "Creating API endpoints"
    - "Modifying API responses"
    - "Reviewing JSON:API compliance"

Use 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 `data` and `errors` in the same response
  • ALWAYS include at least one of: `data`, `errors`, `meta`
  • ALWAYS use `type` and `id` (string) in resource objects
  • NEVER include `id` when 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 `id` or `type` inside `attributes`
  • NEVER include foreign keys in `attributes` - use `relationships`

Relationships

  • ALWAYS include at least one of: `links`, `data`, or `meta`
  • 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 `status` as **string** (e.g., `"400"`, not `400`)
  • ALWAYS include `source.pointer` for 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 `400` for unsupported query parameters
  • MUST return `400` for unsupported `include` paths
  • MUST return `400` for unsupported `sort` fields
  • 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": {..

Read more
Ships withprowler

Prowler is the world’s most widely used Open-Source Cloud Security Platform that automates security and compliance across any cloud environment.

Get the whole plugin
Stats
14,557
Stars
2,311
Forks
Active
Maintenance
Python
Language
Apache-2.0
License
1h ago
Last commit
9y ago
Created

Repo: prowler-cloud/prowler