# API Design Guidelines: [Organisation name]

> Company-wide style guide for designing HTTP APIs, events and webhooks so
> that every API looks as if one team built it. Covers principles, naming,
> URLs, methods and status codes (RFC 9110), errors (RFC 9457 problem
> details), pagination, filtering, versioning, idempotency, concurrency,
> security, rate limiting, data formats, long-running operations, webhooks,
> asynchronous events, documentation with OpenAPI 3.2 and governance.
> Based on common industry practice and the cited IETF standards. Replace
> every `[bracketed]` value; rules marked *Example* are a sensible default
> to keep, change or delete.

<!--
Guidance: How to use this template
- Decide each rule once, write it as MUST / SHOULD / MAY, and give it an ID
  (API-NNN) so reviewers and linters can reference it.
- Start with sections 2 to 7 (principles, naming, methods, errors). They
  cover most review comments. Fill the rest as your APIs need them.
- Every rule that can be checked automatically should have a linter rule
  (section 20). Rules that only live in this document drift.
- Do not copy another company's guidelines wholesale. Pick the decisions
  that fit your consumers and write down why.
-->

## Document control

<!-- Guidance: The guidelines are a governed standard. Version them, and record who approved them and when the next review is due. -->

| Field | Value |
|---|---|
| Document ID | [ARCH-STD-API-001] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Superseded] |
| Owner | [@api-guild-lead] |
| Applies to | [All public and partner APIs; internal APIs from YYYY-MM-DD] |
| Classification | [Internal] |
| Next review | YYYY-MM-DD |
| Last updated | YYYY-MM-DD |

### Approvals

| Role | Name | Decision | Date |
|---|---|---|---|
| Chief architect | [@name] | [Approved / Approved with comments / Rejected] | YYYY-MM-DD |
| Security lead | [@name] | [Decision] | YYYY-MM-DD |
| Developer experience lead | [@name] | [Decision] | YYYY-MM-DD |

## Revision history

<!-- Guidance: One row per published version. Summarise rule changes so API teams can see what is new. -->

| Version | Date | Author | Change |
|---|---|---|---|
| [0.1] | YYYY-MM-DD | [@author] | Initial draft |
| [1.0] | YYYY-MM-DD | [@author] | Approved by architecture board |

## Contents

1. Scope and conformance language
2. Principles
3. Resource naming
4. URL design
5. HTTP methods
6. Status codes
7. Errors
8. Pagination, filtering and sorting
9. Versioning and deprecation
10. Idempotency
11. Concurrency control
12. Security
13. Rate limiting
14. JSON conventions
15. Dates, money and identifiers
16. Long-running operations
17. Webhooks
18. Asynchronous events
19. Documentation
20. Linting and governance
21. Review checklist

## 1. Scope and conformance language

<!-- Guidance: Say which APIs must follow the guidelines and how exceptions are granted. Define the requirement keywords. -->

These guidelines apply to [every HTTP API exposed outside a single team's
deployment unit]. The keywords MUST, MUST NOT, SHOULD, SHOULD NOT and MAY
are used as described in BCP 14 (RFC 2119 and RFC 8174) when they appear
in capitals. A MUST rule can only be broken with a recorded exception
(section 20.3). A SHOULD rule can be broken when the API review records the
reason.

## 2. Principles

<!-- Guidance: A short list of principles that explain the rules. When a case is not covered, reviewers decide by the principles. -->

| ID | Principle | What it means in practice |
|---|---|---|
| API-001 | API first | The contract (OpenAPI or AsyncAPI document) is written and reviewed before implementation starts |
| API-002 | Consistency over cleverness | A consumer who knows one of our APIs can guess how another works |
| API-003 | Designed for the consumer | Resources reflect consumer tasks, not database tables or internal services |
| API-004 | Stable contracts | Published contracts change only in backward-compatible ways within a major version |
| API-005 | Secure by default | Every endpoint requires authentication unless explicitly reviewed as public |
| API-006 | Self-describing | Names, errors and documentation let a developer succeed without contacting the team |

## 3. Resource naming

<!-- Guidance: Most consistency problems are naming problems. Fix the case style, plural rule and vocabulary once. -->

- API-010 Resource collections MUST use plural nouns: `/orders`, `/customers`.
- API-011 Path segments MUST use lowercase kebab-case: `/delivery-slots`.
- API-012 JSON property names MUST use [camelCase / snake_case]; pick one for the whole organisation.
- API-013 Query parameters MUST use the same case style as JSON properties.
- API-014 Names MUST come from the domain's ubiquitous language; avoid technical words such as `data`, `info`, `manager`, `item` without a qualifier.
- API-015 Abbreviations SHOULD be avoided unless universally known (`id`, `url`).
- API-016 Boolean properties SHOULD read as a statement: `isActive`, `hasAttachments`.

*Example:* `GET /customers/{customerId}/payment-methods` returns a list of
`paymentMethod` objects, each with `id`, `type` and `isDefault`.

## 4. URL design

<!-- Guidance: Keep URLs short, predictable and free of implementation details. -->

- API-020 Base URL pattern: `https://api.[example.com]/[domain]/v{major}/`.
- API-021 Nesting MUST NOT exceed two levels: `/orders/{orderId}/lines/{lineId}` is allowed; deeper paths use top-level resources with filters.
- API-022 Identifiers in paths MUST be opaque and URL-safe; never expose database sequence numbers.
- API-023 Actions that do not fit CRUD SHOULD be modelled as sub-resources or state changes (`POST /orders/{id}/cancellation`) rather than verbs in the path. When a verb is unavoidable use the form `POST /orders/{id}:cancel` consistently.
- API-024 URLs MUST NOT contain file extensions, trailing slashes or technology names.

## 5. HTTP methods

<!-- Guidance: Use methods with the semantics defined in RFC 9110. Consumers and intermediaries (caches, proxies, retry libraries) depend on them. -->

| Method | Use for | Safe | Idempotent | Request body |
|---|---|---|---|---|
| GET | Read a resource or collection | Yes | Yes | No |
| HEAD | Read headers only | Yes | Yes | No |
| POST | Create in a collection, or trigger a process | No | No (make it so with API-070) | Yes |
| PUT | Replace a resource completely, or create at a client-chosen URI | No | Yes | Yes |
| PATCH | Partial update using JSON Merge Patch (RFC 7396) or JSON Patch (RFC 6902) | No | Not by default | Yes |
| DELETE | Remove a resource | No | Yes | No |

- API-030 GET MUST NOT change state.
- API-031 PATCH requests MUST declare the media type: `application/merge-patch+json` [default] or `application/json-patch+json`.
- API-032 Endpoints MUST reject unsupported methods with 405 and an `Allow` header.

## 6. Status codes

<!-- Guidance: Restrict the set of codes so clients handle them consistently. RFC 9110 defines their semantics. -->

| Code | When to use |
|---|---|
| 200 OK | Successful read or update that returns a body |
| 201 Created | Resource created; include `Location` |
| 202 Accepted | Long-running operation started (section 16) |
| 204 No Content | Success with no body, for example DELETE |
| 304 Not Modified | Conditional GET matched (section 11) |
| 400 Bad Request | Malformed syntax or invalid parameters |
| 401 Unauthorized | Missing or invalid credentials |
| 403 Forbidden | Authenticated but not allowed |
| 404 Not Found | Resource does not exist or caller may not know it exists |
| 409 Conflict | State conflict, for example duplicate business key |
| 412 Precondition Failed | `If-Match` did not match |
| 415 Unsupported Media Type | Wrong `Content-Type` |
| 422 Unprocessable Content | Well-formed but fails business validation |
| 428 Precondition Required | Update sent without required `If-Match` |
| 429 Too Many Requests | Rate limit exceeded |
| 500 Internal Server Error | Unexpected server error |
| 503 Service Unavailable | Temporary overload or maintenance; include `Retry-After` |

- API-040 APIs MUST NOT return 200 with an error in the body.
- API-041 Clients MUST be able to treat any unlisted 4xx as 400 and any 5xx as 500.

## 7. Errors

<!-- Guidance: One error format across the organisation. RFC 9457 (Problem Details for HTTP APIs) defines the media type application/problem+json and the members type, title, status, detail and instance. Extensions are allowed. -->

- API-050 Error responses MUST use `application/problem+json` (RFC 9457).
- API-051 `type` MUST be a stable URI that resolves to human-readable documentation: `https://api.[example.com]/problems/[problem-name]`.
- API-052 Validation errors MUST list each invalid field in an `errors` extension member.
- API-053 Responses MUST NOT leak stack traces, SQL, internal host names or personal data of other users.
- API-054 Every error response MUST include a correlation identifier (`traceId`) that matches server logs.

```json
{
  "type": "https://api.example.com/problems/validation-failed",
  "title": "Request validation failed",
  "status": 422,
  "detail": "Two fields are invalid.",
  "instance": "/orders/ord_8f2k1",
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "errors": [
    {"pointer": "/deliveryDate", "detail": "Must be today or later."},
    {"pointer": "/lines/0/quantity", "detail": "Must be at least 1."}
  ]
}
```

## 8. Pagination, filtering and sorting

<!-- Guidance: Every collection that can grow MUST be paginated from the first version. Adding pagination later is a breaking change. -->

### 8.1 Pagination

- API-060 Collections MUST be paginated with an opaque cursor: `?pageSize=50&cursor=[token]`.
- API-061 Default `pageSize` is [20], maximum [100]; larger values are clamped, not rejected.
- API-062 Responses return `items` and `nextCursor` (absent on the last page). Total counts are optional because they are expensive.
- API-063 Offset pagination MAY be used only for small, static collections.

### 8.2 Filtering

- API-064 Simple filters use query parameters named after properties: `?status=open&customerId=cus_12`.
- API-065 Multiple values use comma separation: `?status=open,pending`.
- API-066 Range filters use suffixes: `createdAfter`, `createdBefore`.

### 8.3 Sorting

- API-067 Sorting uses `?sort=property` ascending and `?sort=-property` descending; several keys are comma-separated.
- API-068 APIs document which properties are sortable and reject others with 400.

## 9. Versioning and deprecation

<!-- Guidance: Define what counts as breaking and how long consumers have to migrate. Keep the detailed process in the API Versioning and Deprecation Policy and link it. -->

- API-080 Major version in the URL path (`/v1/`); minor, backward-compatible changes do not change the URL.
- API-081 Backward-compatible changes: adding optional request fields, adding response fields, adding endpoints, adding enum values only where consumers were told to tolerate unknown values.
- API-082 Breaking changes: removing or renaming fields, changing types or meaning, making optional input required, changing status codes or authentication.
- API-083 Clients MUST ignore unknown response fields; this rule is stated in every API's documentation.
- API-084 Deprecated operations MUST send the `Deprecation` header (RFC 9745) and, once a removal date is fixed, the `Sunset` header (RFC 8594), with a `Link` to the migration guide.
- API-085 Minimum support after deprecation: [12 months] for public APIs, [6 months] for internal APIs.

## 10. Idempotency

<!-- Guidance: Networks fail. Clients will retry, so unsafe operations must be safe to retry. -->

- API-070 POST operations that create resources or move money MUST accept an `Idempotency-Key` request header (client-generated UUID).
- API-071 The server stores key, request fingerprint and response for at least [24 hours]; a retry with the same key and body returns the original response.
- API-072 Reusing a key with a different body returns 422 with problem type `idempotency-key-reused`.

## 11. Concurrency control

<!-- Guidance: Prevent lost updates when two clients edit the same resource. -->

- API-090 Mutable resources MUST return an `ETag` header.
- API-091 PUT, PATCH and DELETE on such resources MUST require `If-Match`; a missing header returns 428 and a stale one returns 412.
- API-092 GET SHOULD support `If-None-Match` and return 304 when unchanged.

## 12. Security

<!-- Guidance: Align with the security architecture. These rules are the API-specific minimum; the OWASP API Security Top 10 is a useful review aid. -->

- API-100 All traffic MUST use TLS 1.2 or later; plain HTTP is not served.
- API-101 Authentication uses OAuth 2.0 bearer tokens (RFC 6749, RFC 6750) from [identity provider]; machine clients use client credentials.
- API-102 Scopes follow `[domain]:[resource]:[action]`, for example `orders:order:read`. Each operation lists its required scopes in OpenAPI.
- API-103 Authorisation is checked on every object access (object-level), not only per endpoint.
- API-104 Secrets, tokens and personal data MUST NOT appear in URLs, because URLs are logged.
- API-105 Responses return only the fields the caller is allowed to see; no client-side filtering of sensitive data.
- API-106 CORS is allowed only for listed origins; never `*` with credentials.

## 13. Rate limiting

<!-- Guidance: Protect the platform and tell clients their budget before they hit it. -->

- API-110 Every API has a documented quota per client, for example [600 requests per minute].
- API-111 Exceeding it returns 429 with `Retry-After`.
- API-112 Responses SHOULD include remaining-quota headers. Until the IETF RateLimit header fields are published as an RFC, use [`RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`] consistently and document them.

## 14. JSON conventions

<!-- Guidance: Small rules that remove a lot of friction. -->

- API-120 Request and response bodies use `application/json` with UTF-8.
- API-121 Top-level responses are objects, never bare arrays, so fields can be added later.
- API-122 Omit absent optional fields rather than sending `null`, unless `null` carries meaning (for example "clear this value" in merge patch).
- API-123 Enum values use [UPPER_SNAKE_CASE] strings; clients must tolerate unknown values.
- API-124 Empty collections are `[]`, never `null`.

## 15. Dates, money and identifiers

<!-- Guidance: These three types cause most data bugs between systems. -->

| Type | Rule | Example |
|---|---|---|
| Timestamp | RFC 3339 in UTC with `Z` | `"2026-03-14T09:30:00Z"` |
| Date | `YYYY-MM-DD` | `"2026-03-14"` |
| Duration | ISO 8601 duration | `"PT15M"` |
| Money | Object with decimal string amount and ISO 4217 currency | `{"amount": "49.90", "currency": "EUR"}` |
| Country | ISO 3166-1 alpha-2 | `"VN"` |
| Language | BCP 47 tag | `"en-GB"` |
| Identifier | Opaque string with type prefix | `"ord_8f2k1x9"` |

- API-130 Money MUST NOT be sent as a binary floating-point number.

## 16. Long-running operations

<!-- Guidance: Anything that can take longer than a few seconds should not hold the HTTP connection open. -->

- API-140 Return 202 Accepted with `Location: /operations/{operationId}`.
- API-141 The operation resource exposes `status` (`PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`), `createdAt`, `completedAt`, and either `resultUrl` or a problem details `error`.
- API-142 Clients poll with backoff guided by `Retry-After`, or subscribe to a webhook (section 17).

## 17. Webhooks

<!-- Guidance: Webhooks are an API in reverse. Apply the same care to their contracts, security and versioning. -->

- API-150 Payloads are signed with HMAC-SHA256 over timestamp and body; the signature and timestamp travel in headers, and receivers reject messages older than [5 minutes].
- API-151 Delivery is at least once; each event has a unique `id` so receivers can deduplicate.
- API-152 Failed deliveries are retried with exponential backoff for [72 hours], then parked and visible in the developer portal.
- API-153 Payloads carry a minimal event plus a link to fetch current state ("thin" events) unless consumers need the full state.

## 18. Asynchronous events

<!-- Guidance: For events on a broker between systems. Link the event API specification for details. -->

- API-160 Events are named in past tense in the domain language: `OrderPlaced`, `PaymentCaptured`.
- API-161 Event envelopes follow [CloudEvents 1.0] attributes (`id`, `source`, `type`, `time`, `specversion`).
- API-162 Event schemas are registered in [schema registry] and evolve with backward compatibility.
- API-163 Event channels are documented with AsyncAPI.

## 19. Documentation

<!-- Guidance: The OpenAPI document is the contract. Write it first and generate reference documentation from it. -->

- API-170 Every HTTP API publishes an OpenAPI 3.2 document (3.1 is accepted until [YYYY-MM-DD]) in its repository and in the developer portal.
- API-171 Every operation has `operationId`, summary, description, request and response examples, error responses and security requirements.
- API-172 Every API has a getting-started guide with an end-to-end example, authentication steps and a changelog.

## 20. Linting and governance

<!-- Guidance: Make the guidelines enforceable. Automate what you can; review the rest. -->

### 20.1 Automated checks

The organisation ruleset for [Spectral / Redocly / Vacuum] lives in
[repository link]. CI fails a pull request on any MUST violation and warns on
SHOULD violations.

### 20.2 Design review

New APIs and new major versions pass an API design review by [the API
guild] before implementation. Minor changes are reviewed in the pull request
by a certified reviewer.

### 20.3 Exceptions

| ID | API | Rule | Reason | Approved by | Expires |
|---|---|---|---|---|---|
| EX-01 | *Example:* legacy-billing v1 | API-050 | Returns legacy error format until v2 | @chief-architect | YYYY-MM-DD |
| [EX-NN] | [API] | [Rule ID] | [Reason] | [@name] | YYYY-MM-DD |

## 21. Review checklist

<!-- Guidance: Copy this list into the API review ticket. -->

- [ ] OpenAPI document exists, lints clean and was reviewed before implementation.
- [ ] Resource names are plural nouns from the ubiquitous language.
- [ ] Methods and status codes follow sections 5 and 6.
- [ ] Errors use problem details with documented `type` URIs.
- [ ] Collections are paginated with cursors.
- [ ] POST operations with side effects accept `Idempotency-Key`.
- [ ] Mutable resources use `ETag` and `If-Match`.
- [ ] Every operation declares OAuth scopes; object-level authorisation is tested.
- [ ] No secrets or personal data in URLs or logs.
- [ ] Rate limits documented and enforced.
- [ ] Dates, money and identifiers follow section 15.
- [ ] Deprecation plan and changelog in place for any breaking change.

## Standard and licence note

Based on common industry practice for organisation-wide API style guides,
written as original text for this collection and not copied from any
published company guideline. It cites these specifications by number:
RFC 9110 (HTTP Semantics), RFC 9457 (Problem Details for HTTP APIs),
RFC 9745 (Deprecation header), RFC 8594 (Sunset header), RFC 6749 and
RFC 6750 (OAuth 2.0), RFC 7396 and RFC 6902 (patch formats), RFC 3339
(timestamps), BCP 14 (RFC 2119, RFC 8174) and the OpenAPI Specification 3.2.
Obtain those documents for their normative requirements. Template licence:
MIT.
