Files
claudekit/skills/openapi/templates/openapi-3.1-starter.yaml
T
2026-04-19 14:10:38 +07:00

378 lines
12 KiB
YAML

openapi: 3.1.0
info:
title: My API
description: >
Starter spec with 2026-era defaults — RFC 9457 Problem Details,
camelCase JSON, cursor pagination, idempotency keys, rate-limit headers,
and ETag optimistic concurrency. Replace example.com / acme with your own.
version: 1.0.0
contact:
name: API Support
email: support@example.com
license:
name: MIT
identifier: MIT
servers:
- url: http://localhost:3000/v1
description: Local development
- url: https://api.example.com/v1
description: Production
tags:
- name: Users
description: User management
- name: Health
description: Service health checks
security:
- bearerAuth: []
paths:
/health:
get:
tags: [Health]
summary: Health check
operationId: getHealth
security: []
responses:
'200':
description: Service is healthy
content:
application/json:
schema:
type: object
required: [status]
properties:
status: { type: string, example: ok }
timestamp: { type: string, format: date-time }
/users:
get:
tags: [Users]
summary: List users (cursor-paginated)
operationId: listUsers
parameters:
- $ref: '#/components/parameters/CursorParam'
- $ref: '#/components/parameters/LimitParam'
- name: status
in: query
schema: { type: string, enum: [active, inactive] }
responses:
'200':
description: Paginated list of users
headers:
X-RateLimit-Limit: { $ref: '#/components/headers/XRateLimitLimit' }
X-RateLimit-Remaining: { $ref: '#/components/headers/XRateLimitRemaining' }
X-RateLimit-Reset: { $ref: '#/components/headers/XRateLimitReset' }
content:
application/json:
schema: { $ref: '#/components/schemas/UserListResponse' }
'401': { $ref: '#/components/responses/Unauthorized' }
'429': { $ref: '#/components/responses/TooManyRequests' }
post:
tags: [Users]
summary: Create a user
operationId: createUser
parameters:
- $ref: '#/components/parameters/IdempotencyKeyHeader'
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/CreateUserRequest' }
responses:
'201':
description: User created
headers:
Location:
description: URL of the new resource.
schema: { type: string, format: uri }
X-RateLimit-Remaining: { $ref: '#/components/headers/XRateLimitRemaining' }
content:
application/json:
schema: { $ref: '#/components/schemas/User' }
'400': { $ref: '#/components/responses/BadRequest' }
'401': { $ref: '#/components/responses/Unauthorized' }
'409': { $ref: '#/components/responses/Conflict' }
'422': { $ref: '#/components/responses/ValidationError' }
'429': { $ref: '#/components/responses/TooManyRequests' }
/users/{userId}:
parameters:
- name: userId
in: path
required: true
schema: { type: string, example: usr_abc123 }
get:
tags: [Users]
summary: Get a user by ID
operationId: getUser
responses:
'200':
description: User details
headers:
ETag:
description: Opaque version token. Pass as If-Match when updating.
schema: { type: string }
content:
application/json:
schema: { $ref: '#/components/schemas/User' }
'401': { $ref: '#/components/responses/Unauthorized' }
'404': { $ref: '#/components/responses/NotFound' }
patch:
tags: [Users]
summary: Partially update a user (optimistic concurrency via If-Match)
operationId: updateUser
parameters:
- name: If-Match
in: header
required: true
description: ETag from a prior GET. Prevents lost-update collisions.
schema: { type: string }
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/UpdateUserRequest' }
responses:
'200':
description: User updated
headers:
ETag:
description: New version token after the update.
schema: { type: string }
content:
application/json:
schema: { $ref: '#/components/schemas/User' }
'401': { $ref: '#/components/responses/Unauthorized' }
'404': { $ref: '#/components/responses/NotFound' }
'412': { $ref: '#/components/responses/PreconditionFailed' }
'422': { $ref: '#/components/responses/ValidationError' }
delete:
tags: [Users]
summary: Delete a user
operationId: deleteUser
responses:
'204': { description: User deleted }
'401': { $ref: '#/components/responses/Unauthorized' }
'404': { $ref: '#/components/responses/NotFound' }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
apiKeyAuth:
type: apiKey
in: header
name: X-Api-Key
parameters:
CursorParam:
name: cursor
in: query
description: Opaque cursor returned by a previous response.
schema: { type: string }
LimitParam:
name: limit
in: query
description: Maximum items per page.
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
IdempotencyKeyHeader:
name: Idempotency-Key
in: header
required: false
description: >
Client-generated UUID. The server stores the *response* for 24h and
returns the same response for any retry with the same key, so network
errors on unsafe requests can be retried without duplicate side effects.
schema: { type: string, format: uuid }
headers:
XRateLimitLimit:
description: Request quota for the current window.
schema: { type: integer, example: 1000 }
XRateLimitRemaining:
description: Requests remaining in the current window.
schema: { type: integer, example: 942 }
XRateLimitReset:
description: Unix timestamp (seconds) when the quota resets.
schema: { type: integer, example: 1767225600 }
RetryAfterSeconds:
description: Seconds to wait before retrying.
schema: { type: integer, example: 60 }
schemas:
User:
type: object
required: [id, email, name, status, createdAt]
properties:
id: { type: string, example: usr_abc123 }
email: { type: string, format: email, example: jane@example.com }
name: { type: string, example: Jane Doe }
status: { type: string, enum: [active, inactive] }
createdAt: { type: string, format: date-time }
updatedAt: { type: [string, "null"], format: date-time }
CreateUserRequest:
type: object
required: [email, name]
additionalProperties: false
properties:
email: { type: string, format: email, maxLength: 254 }
name: { type: string, minLength: 1, maxLength: 100 }
role:
type: string
enum: [admin, member, viewer]
default: member
UpdateUserRequest:
type: object
additionalProperties: false
properties:
name: { type: string, minLength: 1, maxLength: 100 }
status: { type: string, enum: [active, inactive] }
UserListResponse:
type: object
required: [data, pagination]
properties:
data:
type: array
items: { $ref: '#/components/schemas/User' }
pagination: { $ref: '#/components/schemas/CursorPagination' }
CursorPagination:
type: object
required: [hasMore]
properties:
nextCursor: { type: [string, "null"] }
hasMore: { type: boolean }
ProblemDetails:
type: object
description: RFC 9457 Problem Details for HTTP APIs.
required: [type, title, status]
properties:
type:
type: string
format: uri
description: URI reference identifying the problem type (link to your docs).
example: https://api.example.com/problems/validation-error
title:
type: string
description: Short human-readable summary of the problem class.
example: Validation failed
status:
type: integer
description: HTTP status code.
example: 422
detail:
type: string
description: Human-readable explanation specific to this occurrence.
example: "Field 'email' must be a valid email address."
instance:
type: string
format: uri
description: URI identifying this specific occurrence.
example: /v1/users
errors:
type: array
description: Field-level validation errors (extension member).
items:
type: object
required: [field, message]
properties:
field: { type: string, example: email }
message: { type: string, example: Must be a valid email address. }
code: { type: string, example: invalidFormat }
responses:
BadRequest:
description: Malformed request
content:
application/problem+json:
schema: { $ref: '#/components/schemas/ProblemDetails' }
example:
type: https://api.example.com/problems/bad-request
title: Bad request
status: 400
detail: Request body could not be parsed as JSON.
Unauthorized:
description: Authentication required or invalid
content:
application/problem+json:
schema: { $ref: '#/components/schemas/ProblemDetails' }
example:
type: https://api.example.com/problems/unauthorized
title: Unauthorized
status: 401
detail: Missing or invalid bearer token.
NotFound:
description: Resource not found
content:
application/problem+json:
schema: { $ref: '#/components/schemas/ProblemDetails' }
example:
type: https://api.example.com/problems/not-found
title: Not found
status: 404
detail: User 'usr_abc123' does not exist.
Conflict:
description: Resource conflict (duplicate or state clash)
content:
application/problem+json:
schema: { $ref: '#/components/schemas/ProblemDetails' }
example:
type: https://api.example.com/problems/conflict
title: Conflict
status: 409
detail: A user with that email already exists.
PreconditionFailed:
description: If-Match header did not match current ETag (optimistic concurrency)
content:
application/problem+json:
schema: { $ref: '#/components/schemas/ProblemDetails' }
example:
type: https://api.example.com/problems/precondition-failed
title: Precondition failed
status: 412
detail: The resource was modified since you last fetched it. Re-fetch and retry.
ValidationError:
description: Request validation failed
content:
application/problem+json:
schema: { $ref: '#/components/schemas/ProblemDetails' }
example:
type: https://api.example.com/problems/validation-error
title: Validation failed
status: 422
errors:
- field: email
message: Must be a valid email address.
code: invalidFormat
TooManyRequests:
description: Rate limit exceeded
headers:
Retry-After: { $ref: '#/components/headers/RetryAfterSeconds' }
content:
application/problem+json:
schema: { $ref: '#/components/schemas/ProblemDetails' }
example:
type: https://api.example.com/problems/rate-limited
title: Too many requests
status: 429
detail: Rate limit exceeded. Retry after 60s.