diff --git a/docs/architecture/adr/0041-api-contract-standards.md b/docs/architecture/adr/0041-api-contract-standards.md
new file mode 100644
index 000000000..3d42de828
--- /dev/null
+++ b/docs/architecture/adr/0041-api-contract-standards.md
@@ -0,0 +1,185 @@
+---
+adr: "0041"
+status: Proposed
+date: 2026-10-05
+tags: [server, server-sdk]
+---
+
+# 0041 - API standards for contracts and their evolution
+
+
+
+{/* cspell:ignore kebab */}
+
+## Notation
+
+This ADR uses [RFC 2119](https://www.rfc-editor.org/info/rfc2119/) keywords (`MUST`, `MUST NOT`,
+`SHOULD`, `SHOULD NOT`, `MAY`) deliberately.
+
+## Context and problem statement
+
+[ADR-0036](./0036-service-to-service-api-standards.md) requires service-to-service APIs to conform
+to a documented set of standards, and ADR-0039 builds those standards on IETF RFCs. This ADR sets
+how an API's contract is described and addressed, and how it changes over time without breaking its
+callers: OpenAPI descriptions, paths, versioning, unrecognized input, and deprecation.
+
+Independently deployed services are rolled out, and sometimes rolled back, on their own schedules,
+so a contract has to tolerate callers and services at different versions.
+
+## Considered options
+
+**Versioning**
+
+- **A major version in the path** (`/api/v1/...`), with breaking changes avoided.
+- **No versioning:** additive changes only, as in Bitwarden's public API today.
+- **Version in a header or in the media type.**
+- **A dated `api-version` query parameter,** as in the Azure guidelines.
+
+**Request scope**
+
+- **The organization and user travel as context,** never in the path.
+- **The organization in the path,** as in `/organizations/{id}/users`.
+
+**Unrecognized input**
+
+- **Ignore unrecognized fields, parameters, and headers,** and reject only parameters that shape the
+ result.
+- **Reject anything unrecognized with `400`,** as JSON:API requires.
+
+**Deprecation**
+
+- **Mark it in OpenAPI and announce it in response headers**
+ ([RFC 9745](https://www.rfc-editor.org/info/rfc9745/) `Deprecation`,
+ [RFC 8594](https://www.rfc-editor.org/info/rfc8594/) `Sunset`, and
+ [RFC 8288](https://www.rfc-editor.org/info/rfc8288/) `Link`).
+- **Announce it in documentation and changelogs only.**
+
+## Decision outcome
+
+Chosen options: **a major version in the path**, **scope as context**, **ignoring unrecognized
+input**, and **deprecation in OpenAPI and response headers**.
+
+### OpenAPI
+
+APIs `MUST` be described in OpenAPI.
+
+- Descriptions `MUST` be written for the consumer and free of implementation details.
+- APIs `SHOULD` provide realistic request and response examples, and `SHOULD` document which
+ attributes can be filtered and sorted.
+- Fields that are always present `MUST` be marked `required`, fields that may be null `MUST` be
+ marked `nullable`, and fields set by the server `MUST` be marked `readOnly`. An API `MUST` ignore
+ a read-only field in a request.
+- Every operation `MUST` have an explicit, stable `operationId`. Version 1 operations `SHOULD NOT`
+ include the version, and later versions `MUST` (for example `getGroup`, then `getGroupV2`), so
+ that generated clients do not collide on one method name or depend on names a generator invents.
+
+### Paths
+
+- Paths `MUST` be lowercase kebab-case.
+- The first element `MUST` be a namespace, `api` by default. The next `MUST` be the version,
+ starting at `v1`. The next `MUST` name the resource, and `SHOULD` be its plural (for example
+ `users`). Where the API targets one resource, the next `SHOULD` be its ID.
+- Paths `SHOULD` be traversable: if `/api/v1/users/123/addresses/456` names an address, its parent
+ paths name all of that user's addresses, the user, and all users. This shows how paths fit
+ together. It does not require every parent path to exist.
+
+```text
+/api/v1/users
+/api/v1/users/123
+/api/v1/users/123/addresses
+```
+
+Every request acts on a **subject** and runs inside a **scope**:
+
+- The subject is what the request acts on, and it `MUST` be named in the path.
+- The scope is what the request runs inside, such as the current organization and user. It
+ `MUST NOT` be named in the path, and travels as context instead. Callers `SHOULD` send the current
+ organization and user on every request where each exists.
+- `current` `SHOULD` be used where the subject is whatever the scope names, as in
+ `/api/v1/users/current`.
+
+An identifier in a path is something a caller can try other values of, and the API has to prove on
+every request that the caller may use it. A scope taken from context is established once, where the
+caller authenticates, and one path then serves a resource that can belong to more than one kind of
+owner. Which parts of the scope an operation requires is declared by the operation, and is the
+subject of the forthcoming authentication and authorization standard.
+
+A caller working across several organizations can make one call per organization, use a filter such
+as `filter[organization]=123,456`, or use an advanced query (see ADR-0043). None of these puts the
+organization in the path. An operation can also be declared so that less scope means more, for
+example returning users across every organization the caller belongs to when no organization is in
+context.
+
+### Breaking changes and versioning
+
+APIs `SHOULD NOT` make breaking changes. A breaking change is one that causes a request that is
+valid today to be rejected tomorrow, or a response a caller can process today to become
+unprocessable. In practice:
+
+- adding a required field, or making an optional field required,
+- changing a field's type,
+- adding constraints to a field, or
+- removing a field from a response.
+
+The keyword is `SHOULD NOT` rather than `MUST NOT` because these are service-to-service APIs and we
+own every caller:
+
+- A team `MAY` make a breaking change in place when it can account for every caller, either because
+ the change is coordinated with them or because the rejection surfaces where the caller or user can
+ act on it.
+- Bugs `SHOULD` be fixed in place, even when the fix is technically breaking.
+- Otherwise, a breaking change `MUST` be made in a new version of the API, and the old version
+ deprecated.
+
+Adding a value to a constrained field is not breaking by this definition, but a generated client
+that maps the field to a closed enumeration will fail on a value it has never seen. Consider whether
+callers tolerate unknown values before adding one.
+
+Versioning costs a path segment while an API stays at `v1`. In exchange, a contract that needs to
+change shape has somewhere to go, instead of making every new field optional so that nothing ever
+breaks. The version is in the path because it is then visible in logs, traces, routing rules, and
+`curl` commands, needs no content negotiation, and lets two versions run side by side behind one
+host.
+
+### Unrecognized fields, query parameters, and headers
+
+A service rolled back to fix a defect can receive fields from callers that have already upgraded. So
+that callers do not have to be rolled back too, APIs `MUST` ignore unrecognized fields, query
+parameters, and headers.
+
+The exception is a parameter that shapes the result, because ignoring it returns something other
+than what was asked for. APIs `MUST` reject with `422`:
+
+- a `filter[{name}]`, `sort`, or `fields` parameter naming a field the API does not support,
+- paging parameters when the API does not page, or for a paging style it does not implement, and
+- on a bulk operation, an `atomic` value it does not support.
+
+### Deprecation
+
+- A deprecated API `MUST` be marked `deprecated: true` in OpenAPI, and its description `MUST` name
+ the replacement.
+- Every response from a deprecated API `MUST` include the `Deprecation` and `Sunset` headers, and
+ `SHOULD` include a `Link` header naming the replacement.
+- `Deprecation` is the time the API was deprecated, as `@` followed by seconds since the Unix epoch.
+ `Sunset` is the date it stops working. It `MUST` be a real date, and `MUST NOT` pass without
+ either removal or a published extension.
+
+```http
+Deprecation: @1688169599
+Sunset: Sun, 30 Jun 2024 23:59:59 GMT
+Link: ; rel="successor-version"
+```
+
+### Positive consequences
+
+- Contracts are described precisely enough to generate clients that stay stable across versions.
+- A rolled-back service keeps working with callers that have already moved forward.
+- An unavoidable breaking change has a defined path: a new version, with the old one deprecated on a
+ stated schedule.
+- Paths cannot be used to reach another organization's resources by changing an identifier.
+
+### Negative consequences
+
+- Service-to-service APIs carry a version where Bitwarden's public API does not.
+- Ignoring unrecognized fields means a misspelled optional field is silently dropped.
+- Teams have to judge when a breaking change can be made in place, and that judgement can be wrong.