API contract draft

From business wording and resource names, produce OpenAPI sketches, error codes, and pagination so frontend and backend align early.

The skill can pin auth (Bearer, cookie), idempotency and retries, common 4xx/5xx semantics, and version prefixes with deprecation notes.

Keep output machine-checkable: types, required fields, examples, and enum ranges to cut integration churn.

Split public vs internal APIs: public surfaces emphasize semver and deprecation windows; internal APIs can tighten error shapes for gateways and clients. Define pagination, sort, and filter params once—avoid ad-hoc per-endpoint rules.

  • Errors: unified code / message / details (optional), plus one 4xx and one 5xx example.
  • Security: no secrets in examples; uploads get size and MIME limits.
  • Implementation: remind to sync integration tests and mocks when the contract changes.

OpenAPI 3.0 full route definition

Complete example with requestBody, responses, security, and $ref reuse:

# OpenAPI 3.0 full route definition (with requestBody/responses/security)
openapi: 3.0.3
info:
  title: Items API
  version: 1.0.0
servers:
  - url: https://api.example.com/v1

paths:
  /items:
    post:
      operationId: createItem
      summary: Create item
      tags: [items]
      security:
        - bearerAuth: []          # authentication required
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateItemRequest'
            example:
              title: "Sample item"
              description: "Description text"
      responses:
        '201':
          description: Created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Item'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    CreateItemRequest:
      type: object
      required: [title]
      properties:
        title:  { type: string, minLength: 1, maxLength: 200 }
        description: { type: string, nullable: true }
  responses:
    ValidationError:
      description: Request validation failed
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'

RFC 7807 error format & security

Centralize Problem Details (RFC 7807) in components.responses; each path's responses should $ref it instead of hand-copying the same shape.

// RFC 7807 Problem Details standard error format
// Content-Type: application/problem+json
{
  "type": "https://api.example.com/problems/item-not-found",
  "title": "Item Not Found",
  "status": 404,
  "detail": "The item with ID 'abc123' does not exist",
  "instance": "/requests/req-xyz-789"
}

// Validation error example (400) — extensible fields
{
  "type": "https://api.example.com/problems/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Request body contains invalid fields",
  "errors": [
    { "field": "title", "message": "Title is required" },
    { "field": "price", "message": "Must be a positive number" }
  ]
}

// Server error example (500) — do not leak internal details
{
  "type": "https://api.example.com/problems/internal-error",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred. TraceId: req-abc-123"
  // ❌ Do not expose: stack trace, SQL queries, internal service names
}

Versioning strategy & breaking changes

Pick one strategy for the SKILL: URL prefix (e.g. /v1/), header negotiation, or subdomain; breaking changes need coexistence window, migration guide, and deprecated fields.

// Three API versioning strategies compared

// Strategy 1: URL path versioning (most explicit; recommended for public APIs)
GET /v1/users/123   // v1
GET /v2/users/123   // v2 (can coexist)

// Strategy 2: Request header versioning (cleaner URLs, but complicates caching)
GET /users/123
Accept-Version: 2.0   // or: API-Version: 2

// Strategy 3: Query parameter versioning (not recommended; more caching issues)
GET /users/123?version=2

// Breaking vs non-breaking change decision matrix:
// Non-breaking (backward compatible — no version bump required):
// - Adding optional fields (old clients simply ignore them)
// - Adding new API endpoints
// - Relaxing field validation rules

// Breaking (must bump major version):
// - Removing or renaming fields
// - Changing field types (string → number)
// - Changing enum value semantics
// - Changing auth requirements
// - Changing error codes or HTTP status codes

// Sunset header: notify clients that the old version is being deprecated
HTTP/1.1 200 OK
Sunset: Sat, 31 Dec 2025 23:59:59 GMT
Deprecation: true
Link: <https://api.example.com/v2/docs>; rel="successor-version"

Pact framework contract testing (Consumer-Provider):

// Pact contract test: Consumer defines expectations
import { Pact } from '@pact-foundation/pact';

const provider = new Pact({
  consumer: 'frontend-app',
  provider: 'items-api',
  port: 1234,
});

describe('Items API Contract', () => {
  it('should create item', async () => {
    await provider.addInteraction({
      state: 'user is authenticated',
      uponReceiving: 'a request to create item',
      withRequest: {
        method: 'POST', path: '/v1/items',
        headers: { Authorization: 'Bearer token', 'Content-Type': 'application/json' },
        body: { title: 'Test Item' },
      },
      willRespondWith: {
        status: 201,
        body: { id: like('abc123'), title: 'Test Item', createdAt: like('2025-01-01') },
      },
    });
    // Call the real implementation and Pact verifies it matches the contract
    const result = await createItem({ title: 'Test Item' });
    expect(result.status).toBe(201);
  });
});

Main flow (skill-flow-block)

From product language to a mergeable OpenAPI draft—use this order; the agent can emit a checklist per step.

  [ input: nouns, use cases, auth assumptions ]
        |
        v
  [ resources & paths: pluralization, nesting depth, pagination/filter table ]
        |
        v
  [ schema: required/optional, examples, error components ]
        |
        v
  [ version & deprecation: prefix or header, deprecated + migration notes ]
        |
        v
  [ validate: spectral / CI diff → fix list ]

Path prefix preview

Generate common URL prefix placeholders from the major version (reference only—not a hard standard).

Preview


              

SKILL snippet

---
name: api-contract-draft
description: Generate OpenAPI 3.0 drafts and error format contracts from product descriptions
---
# Input analysis
1. Identify resource nouns and actions; build REST paths (plural collection names, max 2-level nesting)
2. Confirm auth method: Bearer JWT / API Key / OAuth2 scope
3. Confirm pagination strategy: cursor-based (large datasets) or offset (small lists)

# Schema and components
4. requestBody schema: required fields, types, minLength/maxLength/enum constraints
5. Success response schema: required fields, nullable fields (nullable: true), plus example
6. Error responses: centralize in components.responses; paths use $ref references
7. Error format: prefer RFC 7807 application/problem+json

# Versioning and breaking changes
8. Versioning strategy: URL path prefix (/v1/) or Accept-Version header — pick one and document it
9. Breaking changes: removing fields, changing types, changing enums, changing auth — must bump major version
10. Non-breaking: adding optional fields, adding endpoints — can append to existing version
11. Deprecation window: Sunset / Deprecation response headers + migration doc link

# Contract testing
12. Consumer side: use Pact to define expectations and generate pact files
13. Provider side: verify pact files in CI to block breaking changes
14. After contract changes: sync integration tests and API mocks
15. Spectral rule set validates OpenAPI spec; resolve every violation — never silently ignore

Back to skills More skills