Claude Code Plugins

Community-maintained marketplace

Feedback

Author and validate OpenAPI 3.1 specifications for REST API design, following API-first and contract-first development practices

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name openapi-authoring
description Author and validate OpenAPI 3.1 specifications for REST API design, following API-first and contract-first development practices
allowed-tools Read, Write, Edit, Glob, Grep, Bash

OpenAPI Authoring Skill

When to Use This Skill

Use this skill when:

  • Openapi Authoring tasks - Working on author and validate openapi 3.1 specifications for rest api design, following api-first and contract-first development practices
  • Planning or design - Need guidance on Openapi Authoring approaches
  • Best practices - Want to follow established patterns and standards

Overview

Author OpenAPI 3.1 specifications for REST API design using API-first methodology.

OpenAPI 3.1 Structure

Root Document

openapi: "3.1.0"

info:
  title: "{Service Name} API"
  version: "1.0.0"
  description: |
    {Service description and purpose}
  contact:
    name: "{Team Name}"
    email: "{team@company.com}"
  license:
    name: "MIT"
    identifier: "MIT"

servers:
  - url: "https://api.example.com/v1"
    description: "Production"
  - url: "https://api.staging.example.com/v1"
    description: "Staging"
  - url: "http://localhost:5000/v1"
    description: "Local development"

tags:
  - name: "{Resource}"
    description: "Operations for {resource} management"

paths:
  # Path definitions

components:
  # Reusable components

security:
  - bearerAuth: []

Path Operations

paths:
  /resources:
    get:
      operationId: "listResources"
      summary: "List all resources"
      description: "Retrieves a paginated list of resources"
      tags:
        - Resources
      parameters:
        - $ref: "#/components/parameters/PageNumber"
        - $ref: "#/components/parameters/PageSize"
        - $ref: "#/components/parameters/SortBy"
      responses:
        "200":
          description: "Successful response"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourceListResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"

    post:
      operationId: "createResource"
      summary: "Create a new resource"
      description: "Creates a new resource with the provided data"
      tags:
        - Resources
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateResourceRequest"
      responses:
        "201":
          description: "Resource created"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourceResponse"
          headers:
            Location:
              description: "URL of the created resource"
              schema:
                type: string
                format: uri
        "400":
          $ref: "#/components/responses/BadRequest"
        "422":
          $ref: "#/components/responses/UnprocessableEntity"

  /resources/{resourceId}:
    parameters:
      - $ref: "#/components/parameters/ResourceId"

    get:
      operationId: "getResource"
      summary: "Get a resource by ID"
      description: "Retrieves a single resource by its unique identifier"
      tags:
        - Resources
      responses:
        "200":
          description: "Successful response"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourceResponse"
        "404":
          $ref: "#/components/responses/NotFound"

    put:
      operationId: "updateResource"
      summary: "Update a resource"
      description: "Replaces the entire resource with the provided data"
      tags:
        - Resources
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateResourceRequest"
      responses:
        "200":
          description: "Resource updated"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourceResponse"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"

    patch:
      operationId: "patchResource"
      summary: "Partially update a resource"
      description: "Updates specific fields of the resource"
      tags:
        - Resources
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PatchResourceRequest"
      responses:
        "200":
          description: "Resource patched"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourceResponse"
        "404":
          $ref: "#/components/responses/NotFound"

    delete:
      operationId: "deleteResource"
      summary: "Delete a resource"
      description: "Permanently removes a resource"
      tags:
        - Resources
      responses:
        "204":
          description: "Resource deleted"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"

Component Schemas

components:
  schemas:
    # Request schemas
    CreateResourceRequest:
      type: object
      required:
        - name
        - type
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: "Resource name"
          example: "My Resource"
        type:
          $ref: "#/components/schemas/ResourceType"
        description:
          type: string
          maxLength: 500
          description: "Optional description"
        metadata:
          type: object
          additionalProperties: true
          description: "Custom metadata key-value pairs"

    UpdateResourceRequest:
      allOf:
        - $ref: "#/components/schemas/CreateResourceRequest"

    PatchResourceRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
        description:
          type: string
          maxLength: 500
      minProperties: 1

    # Response schemas
    ResourceResponse:
      type: object
      required:
        - id
        - name
        - type
        - createdAt
        - updatedAt
      properties:
        id:
          type: string
          format: uuid
          description: "Unique identifier"
          example: "550e8400-e29b-41d4-a716-446655440000"
        name:
          type: string
          description: "Resource name"
        type:
          $ref: "#/components/schemas/ResourceType"
        description:
          type: string
        metadata:
          type: object
          additionalProperties: true
        createdAt:
          type: string
          format: date-time
          description: "Creation timestamp (ISO 8601)"
        updatedAt:
          type: string
          format: date-time
          description: "Last update timestamp (ISO 8601)"
        _links:
          $ref: "#/components/schemas/ResourceLinks"

    ResourceListResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ResourceResponse"
        pagination:
          $ref: "#/components/schemas/Pagination"
        _links:
          $ref: "#/components/schemas/PaginationLinks"

    # Enums
    ResourceType:
      type: string
      enum:
        - standard
        - premium
        - enterprise
      description: "Type of resource"

    # Common schemas
    Pagination:
      type: object
      required:
        - page
        - pageSize
        - totalItems
        - totalPages
      properties:
        page:
          type: integer
          minimum: 1
          description: "Current page number"
        pageSize:
          type: integer
          minimum: 1
          maximum: 100
          description: "Items per page"
        totalItems:
          type: integer
          minimum: 0
          description: "Total number of items"
        totalPages:
          type: integer
          minimum: 0
          description: "Total number of pages"

    ResourceLinks:
      type: object
      properties:
        self:
          type: string
          format: uri
        collection:
          type: string
          format: uri

    PaginationLinks:
      type: object
      properties:
        self:
          type: string
          format: uri
        first:
          type: string
          format: uri
        prev:
          type: string
          format: uri
        next:
          type: string
          format: uri
        last:
          type: string
          format: uri

    # Error schemas
    ErrorResponse:
      type: object
      required:
        - type
        - title
        - status
      properties:
        type:
          type: string
          format: uri
          description: "URI reference identifying the problem type"
        title:
          type: string
          description: "Short, human-readable summary"
        status:
          type: integer
          description: "HTTP status code"
        detail:
          type: string
          description: "Human-readable explanation"
        instance:
          type: string
          format: uri
          description: "URI reference identifying the specific occurrence"
        errors:
          type: array
          items:
            $ref: "#/components/schemas/ValidationError"

    ValidationError:
      type: object
      required:
        - field
        - message
      properties:
        field:
          type: string
          description: "Field path (e.g., 'name' or 'address.city')"
        message:
          type: string
          description: "Validation error message"
        code:
          type: string
          description: "Error code for programmatic handling"

Parameters and Responses

components:
  parameters:
    ResourceId:
      name: resourceId
      in: path
      required: true
      description: "Resource unique identifier"
      schema:
        type: string
        format: uuid

    PageNumber:
      name: page
      in: query
      description: "Page number (1-indexed)"
      schema:
        type: integer
        minimum: 1
        default: 1

    PageSize:
      name: pageSize
      in: query
      description: "Number of items per page"
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20

    SortBy:
      name: sortBy
      in: query
      description: "Sort field and direction"
      schema:
        type: string
        pattern: "^[a-zA-Z]+:(asc|desc)$"
        example: "createdAt:desc"

    IfMatch:
      name: If-Match
      in: header
      description: "ETag for optimistic concurrency"
      schema:
        type: string

  responses:
    BadRequest:
      description: "Bad Request - Invalid input"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            type: "https://api.example.com/problems/bad-request"
            title: "Bad Request"
            status: 400
            detail: "The request body is malformed"

    Unauthorized:
      description: "Unauthorized - Authentication required"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    Forbidden:
      description: "Forbidden - Insufficient permissions"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    NotFound:
      description: "Not Found - Resource does not exist"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    Conflict:
      description: "Conflict - Resource state conflict"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

    UnprocessableEntity:
      description: "Unprocessable Entity - Validation failed"
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            type: "https://api.example.com/problems/validation-error"
            title: "Validation Error"
            status: 422
            detail: "One or more validation errors occurred"
            errors:
              - field: "name"
                message: "Name is required"
                code: "required"

    TooManyRequests:
      description: "Too Many Requests - Rate limit exceeded"
      headers:
        Retry-After:
          description: "Seconds until rate limit resets"
          schema:
            type: integer
        X-RateLimit-Limit:
          description: "Requests per window"
          schema:
            type: integer
        X-RateLimit-Remaining:
          description: "Requests remaining"
          schema:
            type: integer
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"

Security Schemes

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: "JWT Bearer token authentication"

    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: "API key for service-to-service auth"

    oauth2:
      type: oauth2
      description: "OAuth 2.0 authentication"
      flows:
        authorizationCode:
          authorizationUrl: "https://auth.example.com/authorize"
          tokenUrl: "https://auth.example.com/token"
          refreshUrl: "https://auth.example.com/refresh"
          scopes:
            "read:resources": "Read access to resources"
            "write:resources": "Write access to resources"
            "admin:resources": "Administrative access"

C# Models for OpenAPI

using System.Text.Json.Serialization;

namespace SpecDrivenDevelopment.OpenApi;

/// <summary>
/// Represents an OpenAPI specification document
/// </summary>
public record OpenApiSpec
{
    public required string OpenApi { get; init; } = "3.1.0";
    public required OpenApiInfo Info { get; init; }
    public List<OpenApiServer> Servers { get; init; } = [];
    public Dictionary<string, OpenApiPathItem> Paths { get; init; } = [];
    public OpenApiComponents? Components { get; init; }
    public List<OpenApiSecurityRequirement> Security { get; init; } = [];
    public List<OpenApiTag> Tags { get; init; } = [];
}

public record OpenApiInfo
{
    public required string Title { get; init; }
    public required string Version { get; init; }
    public string? Description { get; init; }
    public OpenApiContact? Contact { get; init; }
    public OpenApiLicense? License { get; init; }
}

public record OpenApiContact
{
    public string? Name { get; init; }
    public string? Email { get; init; }
    public string? Url { get; init; }
}

public record OpenApiLicense
{
    public required string Name { get; init; }
    public string? Identifier { get; init; }
    public string? Url { get; init; }
}

public record OpenApiServer
{
    public required string Url { get; init; }
    public string? Description { get; init; }
    public Dictionary<string, OpenApiServerVariable>? Variables { get; init; }
}

public record OpenApiServerVariable
{
    public required string Default { get; init; }
    public List<string>? Enum { get; init; }
    public string? Description { get; init; }
}

public record OpenApiTag
{
    public required string Name { get; init; }
    public string? Description { get; init; }
}

public record OpenApiPathItem
{
    public string? Summary { get; init; }
    public string? Description { get; init; }
    public OpenApiOperation? Get { get; init; }
    public OpenApiOperation? Post { get; init; }
    public OpenApiOperation? Put { get; init; }
    public OpenApiOperation? Patch { get; init; }
    public OpenApiOperation? Delete { get; init; }
    public List<OpenApiParameter>? Parameters { get; init; }
}

public record OpenApiOperation
{
    public required string OperationId { get; init; }
    public string? Summary { get; init; }
    public string? Description { get; init; }
    public List<string>? Tags { get; init; }
    public List<OpenApiParameter>? Parameters { get; init; }
    public OpenApiRequestBody? RequestBody { get; init; }
    public required Dictionary<string, OpenApiResponse> Responses { get; init; }
    public List<OpenApiSecurityRequirement>? Security { get; init; }
    public bool Deprecated { get; init; }
}

public record OpenApiParameter
{
    public required string Name { get; init; }

    [JsonConverter(typeof(JsonStringEnumConverter))]
    public required ParameterLocation In { get; init; }

    public string? Description { get; init; }
    public bool Required { get; init; }
    public OpenApiSchema? Schema { get; init; }

    [JsonPropertyName("$ref")]
    public string? Ref { get; init; }
}

public enum ParameterLocation
{
    Query,
    Header,
    Path,
    Cookie
}

public record OpenApiRequestBody
{
    public string? Description { get; init; }
    public required Dictionary<string, OpenApiMediaType> Content { get; init; }
    public bool Required { get; init; }
}

public record OpenApiResponse
{
    public required string Description { get; init; }
    public Dictionary<string, OpenApiMediaType>? Content { get; init; }
    public Dictionary<string, OpenApiHeader>? Headers { get; init; }

    [JsonPropertyName("$ref")]
    public string? Ref { get; init; }
}

public record OpenApiMediaType
{
    public OpenApiSchema? Schema { get; init; }
    public object? Example { get; init; }
    public Dictionary<string, OpenApiExample>? Examples { get; init; }
}

public record OpenApiExample
{
    public string? Summary { get; init; }
    public string? Description { get; init; }
    public object? Value { get; init; }
}

public record OpenApiHeader
{
    public string? Description { get; init; }
    public OpenApiSchema? Schema { get; init; }
}

public record OpenApiSchema
{
    public string? Type { get; init; }
    public string? Format { get; init; }
    public string? Description { get; init; }
    public List<string>? Enum { get; init; }
    public object? Default { get; init; }
    public object? Example { get; init; }
    public List<string>? Required { get; init; }
    public Dictionary<string, OpenApiSchema>? Properties { get; init; }
    public OpenApiSchema? Items { get; init; }
    public bool? Nullable { get; init; }
    public int? MinLength { get; init; }
    public int? MaxLength { get; init; }
    public int? Minimum { get; init; }
    public int? Maximum { get; init; }
    public string? Pattern { get; init; }
    public List<OpenApiSchema>? AllOf { get; init; }
    public List<OpenApiSchema>? OneOf { get; init; }
    public List<OpenApiSchema>? AnyOf { get; init; }
    public bool? AdditionalProperties { get; init; }

    [JsonPropertyName("$ref")]
    public string? Ref { get; init; }
}

public record OpenApiComponents
{
    public Dictionary<string, OpenApiSchema>? Schemas { get; init; }
    public Dictionary<string, OpenApiParameter>? Parameters { get; init; }
    public Dictionary<string, OpenApiResponse>? Responses { get; init; }
    public Dictionary<string, OpenApiSecurityScheme>? SecuritySchemes { get; init; }
}

public record OpenApiSecurityScheme
{
    public required string Type { get; init; }
    public string? Scheme { get; init; }
    public string? BearerFormat { get; init; }
    public string? Description { get; init; }
    public string? Name { get; init; }
    public string? In { get; init; }
    public OpenApiOAuthFlows? Flows { get; init; }
}

public record OpenApiOAuthFlows
{
    public OpenApiOAuthFlow? AuthorizationCode { get; init; }
    public OpenApiOAuthFlow? ClientCredentials { get; init; }
}

public record OpenApiOAuthFlow
{
    public required string AuthorizationUrl { get; init; }
    public required string TokenUrl { get; init; }
    public string? RefreshUrl { get; init; }
    public required Dictionary<string, string> Scopes { get; init; }
}

public record OpenApiSecurityRequirement : Dictionary<string, List<string>>;

OpenAPI Design Patterns

Versioning Strategy

versioning_strategies:
  url_path:
    description: "Version in URL path"
    example: "/v1/resources"
    pros:
      - "Explicit and visible"
      - "Easy to route"
      - "Cache-friendly"
    cons:
      - "URL changes on major version"

  header:
    description: "Version in custom header"
    example: "X-API-Version: 2023-01-15"
    pros:
      - "Clean URLs"
      - "Fine-grained control"
    cons:
      - "Less discoverable"
      - "Harder to test in browser"

  query_parameter:
    description: "Version as query parameter"
    example: "/resources?version=2"
    pros:
      - "Easy to specify"
      - "Fallback to default"
    cons:
      - "Cache complications"
      - "Less clean"

  recommended: "url_path"
  rationale: "Most explicit, widely adopted, cache-friendly"

Pagination Patterns

pagination_patterns:
  offset_based:
    description: "Traditional page/pageSize pagination"
    parameters:
      - "page (1-indexed)"
      - "pageSize (default 20, max 100)"
    response:
      pagination:
        page: 2
        pageSize: 20
        totalItems: 150
        totalPages: 8
    pros:
      - "Simple to implement"
      - "Random access to pages"
    cons:
      - "Inconsistent with concurrent writes"
      - "Performance degrades at high offsets"

  cursor_based:
    description: "Cursor/continuation token pagination"
    parameters:
      - "cursor (opaque token)"
      - "limit (default 20, max 100)"
    response:
      pagination:
        nextCursor: "eyJpZCI6MTIzfQ=="
        hasMore: true
    pros:
      - "Consistent with concurrent writes"
      - "Better performance at scale"
    cons:
      - "No random access"
      - "Harder to implement"

  recommended: "cursor_based for large datasets, offset_based for small"

Error Handling (RFC 7807)

error_handling:
  standard: "RFC 7807 Problem Details"
  content_type: "application/problem+json"

  structure:
    type: "URI reference identifying problem type"
    title: "Short human-readable summary"
    status: "HTTP status code"
    detail: "Human-readable explanation specific to this occurrence"
    instance: "URI reference to specific occurrence"

  extensions:
    errors: "Array of field-level validation errors"
    traceId: "Correlation ID for debugging"

  example:
    type: "https://api.example.com/problems/validation-error"
    title: "Validation Error"
    status: 422
    detail: "The request contains invalid data"
    instance: "/resources/123"
    traceId: "abc-123-xyz"
    errors:
      - field: "email"
        message: "Invalid email format"
        code: "invalid_format"

Validation Checklist

openapi_validation_checklist:
  structure:
    - "Valid OpenAPI 3.1.0 syntax"
    - "All required fields present (openapi, info, paths)"
    - "No undefined $ref references"
    - "Consistent naming conventions"

  operations:
    - "Every operation has unique operationId"
    - "All operations have summary and description"
    - "All operations tagged appropriately"
    - "All path parameters defined"
    - "Response codes cover success and error cases"

  schemas:
    - "All schemas have descriptions"
    - "Required fields explicitly listed"
    - "Examples provided for complex types"
    - "Enums documented with descriptions"
    - "String formats specified (uuid, date-time, email, uri)"

  security:
    - "Security schemes defined"
    - "Operations specify security requirements"
    - "OAuth scopes documented"

  documentation:
    - "API description explains purpose"
    - "Contact information provided"
    - "Server URLs for all environments"
    - "Tags organized logically"

  best_practices:
    - "Use RFC 7807 for errors"
    - "Consistent pagination approach"
    - "HATEOAS links where appropriate"
    - "Idempotency keys for POST operations"
    - "ETag/If-Match for optimistic concurrency"

References

  • references/openapi-patterns.md - Common OpenAPI design patterns
  • references/api-guidelines.md - API design guidelines and standards

Last Updated: 2025-12-26