mirror of
https://github.com/duthaho/claudekit.git
synced 2026-06-12 13:14:56 +03:00
378 lines
12 KiB
YAML
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.
|