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

Claude Code

$ npx -y skills add prowler-cloud/prowler --skill jsonapi --agent claude-code

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.1.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

When a rule is unclear, check the official spec (https://jsonapi.org/format/) for the specific topic before deciding.

---

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": {"page": 1, "pages": 5, "count": 100},
    "version": "v1"
  }
}

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": {...}}` | | 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"
Read more
@prowler-cloud
Ships withprowler

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

15k39 skills1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
15.2k
Star history of prowler-cloud/prowler on GitHub, a chart by star-history.com

Other skills on prowler.

ai-sdk-5

ai-sdk-5

Vercel AI SDK patterns for the installed major (ui/package.json: `ai` 6.x, `@ai-sdk/react` 3.x). The skill name is historical;…

django-drf

django-drf

Django REST Framework patterns. Trigger: When implementing generic DRF APIs (ViewSets, serializers, routers, permissions,…

gh-aw

gh-aw

Create and maintain GitHub Agentic Workflows (gh-aw) for Prowler. Trigger: When creating agentic workflows, modifying gh-aw…

nextjs-16

nextjs-16

Next.js 16 App Router patterns. Trigger: When working in Next.js App Router (app/), Server Components vs Client Components,…

More skills from other plugins.

api-security

api-security

Use for authorized security assessment of REST, GraphQL, WebSocket, or SOAP APIs, including discovery, authentication,…

@zhaoxuya520@zhaoxuya520Install Skill
nemoclaw-user-guide

nemoclaw-user-guide

Find official NemoClaw documentation for installation, configuration, operation, or troubleshooting. Also handles requested docs…

nemoclaw10d23k
@nvidia@nvidiaInstall Skill

Questions about jsonapi.

Which coding agents can use jsonapi?
jsonapi installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.