API Guidelines
Request for comment
Table of contents
Table of contents
- 1. Introduction
- 2. Core Principles
- 3. Naming & Structure Conventions
- 4. Communication Standards
- 5. Data Formatting Standards
- 6. Advanced Design Patterns
- 7. Alternative Approaches
- FAQ
- Appendix
- I. Breaking changes
- II. Deprecation rules
1. Introduction
Welcome to Enturs API guidelines, designed to ensure a consistent and robust approach to developing RESTful APIs. REST (Representational State Transfer) is an architectural style that uses standard HTTP methods to create scalable, lightweight, and maintainable services.
1.1 Purpose of the Entur API guidelines
- To establish common guidelines and best practices for API design
- To simplify integration between different systems and applications
- To contribute to a secure, efficient and user-friendly implementation of APIs
1.2 Intended audience
This guide is for developers, architects, and technical designers who work on the design, implementation, and maintenance of APIs. By following these guidelines, we ensure APIs are easy to understand, consistent in structure, and simple to integrate with other systems.
1.3 Contributing
For details on how to contribute to these guidelines, see the contributing guide.
1.4 Requirement Levels
Throughout this document, these requirement levels are used:
- MUST: This is an absolute requirement
- SHOULD: There may be valid reasons to ignore this requirement, but implications must be understood and carefully weighed
- MAY: This is optional
1.5 Linter Coverage
Throughout this document, rules are marked with the following indicators:
2. Core Principles
2.1 General Design Principles
-
Consistency - Make sure the API is easy to understand and predictable -
HTTP Methods - API operations MUST use standard HTTP methods (GET, POST, PUT, PATCH, DELETE) -
Data Format - API endpoints SHOULD support the JSON data format, unless there is a good reason not to -
APIs MUST be documented using OpenAPI 3.x
-
Documentation - All functionality SHOULD be documented with examples and descriptions -
Encryption: All communication MUST be over HTTPS -
You SHOULD not use localhost (or 127.0.0.1) host names in info.servers. -
info.titleMUST be non-empty and MUST not contain the word ‘api’. -
Don’t use info.titleto declare your APIs lifecycle status (draft, beta, deprecated). Instead follow the lifecycle guidelines.
2.2 Development Approach
-
We encourage to follow a Contract-First workflow: - Create the API specification before implementing the API
- The specification is the primary reference for both development and documentation
- Update the specification throughout development to reflect changes
-
Lint your API spec
-
Differentiate API specifications based on the target audience. The target audience of an API specification is declared in info.x-entur-metadata.audience. For details, see: 2.4 Entur Metadata. There are different requirements based on the audience of your API.-
open: Available to anyone, without requiring a contractual relationship with Entur-
MUST follow these guidelines -
MUST be published to the developer portal
-
-
partner: Available to partners of Entur-
MUST follow these guidelines -
MUST be published to the developer portal
-
-
internal: Available to other teams within Entur-
SHOULD follow these guidelines -
MUST be published to the developer portal
-
-
private: Only available from within the same component/application. Examples: A backend API made specifically for a frontend developed by the same team, or administration and management endpoints used for housekeeping within the team.-
MAY follow these guidelines -
MAY be published to the developer portal
-
-
2.3 Authentication and Authorization
2.3.1 About security in OpenAPI specs
Use securitySchemes inside components to define security schemes. Then, refer to schemes using security either at the root level of the spec,to apply security to all operations, or under individual operations which are secured, which replaces the root config.
See:
- https://spec.openapis.org/oas/v3.1.2.html#security-scheme-object
- https://spec.openapis.org/oas/v3.1.2.html#security-requirement-object
2.3.2 Partner- and Internal endpoints
-
These endpoints are secured using JWT tokens. This MUST be documented using a jwtscheme.
Example:
{
"security": [{ "jwt": [] }],
"components": {
"securitySchemes": {
"jwt": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" }
}
}
}
More information on Authentication
2.3.3 Documenting permissions for Partner endpoints
-
Endpoints that require permissions MUST be documented with the x-entur-permissionsextension.
Example:
{
...,
"paths": {
"/items": {
"get": {
"operationId": "getItems",
...,
"x-entur-permissions": {
"value": "items:les"
}
},
"post": {
"operationId": "createItem",
...,
"x-entur-permissions": {
"value": "items:opprett"
}
}
}
}
}
where items:les means that you need the access les on the operation items.
You can also specify that an endpoint requires multiple permissions, for example:
{
"x-entur-permissions": {
"value": {
"all": [
"organisations:les",
{
"any": [
"items:opprett",
"items-global:opprett"
]
}
]
}
}
}
This means that the endpoint requires organisations:les, as well as either items:opprett or items-global:opprett.
If you need to explain the required permissions in more detail, you can declare a description field at the root.
{
"x-entur-permissions": {
"description": "To call this endpoint you need access to read organisations, as well as creating items for your organisation.",
"value": {
"all": [
"organisations:les",
{
"any": [
"items:opprett",
"items-global:opprett"
]
}
]
}
}
}
2.4 Entur Metadata
All OpenAPI specifications published to Enturs developer portal must declare a block x-entur-metadata in the info section of the specification.
| Field name | Type | Description |
|---|---|---|
| id | string | REQUIRED. Unique id for this specification. Read more. |
| audience | string |
REQUIRED. Who this specification is targeted to. Must be one of "open", "partner", "internal", "private"
|
| owner | string | REQUIRED. The Entur team responsible for this specification. Read more. |
| parentId | string | Id of the parent specification, used when merging. Read more. |
| devExtensions | [string] | OpenAPI extensions that are used for development, that should not be visible to consumers. Read more. |
Example:
{
"info": {
"x-entur-metadata": {
"id": "items",
"owner": "team-api",
"audience": "partner"
}
}
}
2.4.1 Identifying a specification
-
OpenAPI specifications MUST use the idproperty in thex-entur-metadataextension in theinfosection. -
It MUST be in lower kebab-case and contain only dashes, digits and letters (a-z). -
It SHOULD NOT have a -idsuffix (or prefix).
Choosing an id The id is used to uniquely identify a specification. Each id results in an entry in the Developer Portal API catalogue and an entry in the linting results. It is also used to create the URL where the specification is available for download from the Developer Portal. Because of this, the id should not change over time, and it should be suitable for external use. The current api title (in kebab-case) could be a good id, but you should not update the id if the title changes in the future.
See also 3.2.2 API spec versioning.
2.4.2 Specification owner
-
OpenAPI specifications SHOULD use the ownerproperty in thex-entur-metadataextension in theinfosection.
The owner field declares which team is responsible for maintaining a specification. It should follow the same format as the owner field in the platform orchestrator.
2.4.3 Merging specifications
Sometimes it is useful to develop some API endpoints separately, but still document them externally as a single specification. To achieve this, you can use the x-entur-metadata field parentId. This field may contain a reference the x-entur-metadata.id field in another published specification.
Nesting is not supported. parentId must refer to a specification that does not itself declare a parentId.
If there are problems with the merging, for example conflicts between the schemas, none of the specifications will be exposed.
Limitations
There are some limitations when merging specifications:
-
servers.urlin all specs must be a subpath of the declaredservers.urlin the “parent specification”. The merged specification will use theserversdefinition from the parent specification, and the subpath of the other specifications will be added to the operation paths. Ifserversare not compatible, none of the specifications will be published. - Root level
securitymust be identical in all specs to be merged. - If there are multiple non-equal schemas with the same name, they will be namespaced with the
x-entur-metadata.idfield. So if multiple specs declare a schemaItemthat differ from each other, there will be one schemaItem_alphaand one schemaItem_beta.
Example
For example, say you have three microservices, alpha, beta and gamma:
Microservice alpha publishes the specification:
{
"info": {
"x-entur-metadata": {
"id": "alpha"
}
},
...
}
Microservice beta publishes the specification:
{
"info": {
"x-entur-metadata": {
"id": "beta",
"parentId": "alpha"
}
},
...
}
Microservice gamma publishes the specification:
{
"info": {
"x-entur-metadata": {
"id": "gamma",
"parentId": "alpha"
}
},
...
}
Here, only 1 specification will be shown on the Developer Portal, which is a combination of alpha, beta and gamma. Since alpha is the parent specification, the combined specification will use alpha as the base, so fields like info.title will be picked from there.
2.4.4 Development-only OpenAPI extensions
Sometimes there may be a need for adding OpenAPI extensions to a specification that are only meant for the development process, for example for generating server interfaces with OpenAPIGenerator. These extensions are only relevant for the team creating the API, not for consumers. To hide extensions like these from the OpenAPI specification that is served from the Developer Portal, use the field x-entur-metadata.devExtensions.
Example
The extension x-kotlin-implements will be stripped before the specification is published to the Developer Portal.
{
"info": {
"x-entur-metadata": {
...,
"devExtensions": ["x-kotlin-implements"]
}
},
"components": {
"schemas": {
"Item": {
"type": "object",
"x-kotlin-implements": ["com.example.Item"],
...
}
}
}
}
2.5 Lifecycle
All APIs, and endpoints within an API, follow a lifecycle with different stages that have different properties. The following sections describe a standardized way to declare an API’s lifecycle status in its specification. Following this standard has several benefits:
- It is easier for consumers to understand what they can expect from an API in terms of stability.
- It is easier to enforce Enturs common policy on backwards compatibility and deprecation.
Lifecycle status may be declared both for the entire API specification, or for individual operations (endpoints). At the API level, use the following extension fields in the info block:
| Field name | Type | Description |
|---|---|---|
| x-stability-level | string | The current lifecycle status of the API. Must be one of: draft, beta, stable. Default value is stable. |
| x-deprecated | boolean |
true if the API is deprecated. Default value is false. |
| x-sunset | string | The date at which the API will be discontinued, in the format YYYY-MM-DD. |
For individual operations, use the following extension fields in an Operation object. All of the fields inherit default values from the API level.
| Field name | Type | Description |
|---|---|---|
| deprecated | boolean | Already exists in the OAS. true if the endpoint is deprecated. Default value is info.x-deprecated. |
| x-stability-level | string | The current lifecycle status of the endpoint. Must be one of: draft, beta, stable. Default value is info.x-stability-level. |
| x-sunset | string | The date at which the endpoint will be removed, in the format YYYY-MM-DD. Default value is info.x-sunset. |
Below is a description of what values these fields should have during an API’s or endpoint’s lifecycle.
2.5.1 Design and development
The shape of the API is being figured out. Changes happen often. The API may not even be implemented yet.
-
The field x-stability-levelMUST have the valuedraft -
The field deprecated/x-deprecatedMUST have the valuefalse(or be empty) -
The field x-sunsetMUST be empty -
You MAY make breaking changes
2.5.2 Testing and feedback
Changes happen, and more design and development may be necessary.
-
The field x-stability-levelMUST have the valuebeta -
The field deprecated/x-deprecatedMUST have the valuefalse(or be empty) -
The field x-sunsetMUST be empty -
You MAY make breaking changes
2.5.3 Production use
The API is relied upon by consumers. Changes may happen, as long as they are not breaking.
-
The field x-stability-levelMUST have the valuestable(or be empty) -
The field deprecated/x-deprecatedMUST have the valuefalse(or be empty) -
The field x-sunsetMUST be empty -
You MUST NOT make breaking changes
2.5.4 Deprecation
The API is marked as deprecated and consumers are encouraged to move to a new version. Only critical changes will happen.
-
The field x-stability-levelMUST have the valuestable(or be empty) -
The field deprecated/x-deprecatedMUST have the valuetrue -
The field x-sunsetMUST be filled with a date that follows Enturs rules for deprecation -
You MUST NOT make breaking changes
2.5.5 Sunsetting
The API is removed.
-
When the date in x-sunsethas passed, the endpoint or API SHOULD be removed. If the API is not ready to be removed yet, the value should be updated.
2.5.6 Notes
-
Individual endpoints MAY override x-stability-levelto an earlier stage in the lifecycle than the API, but NOT later.- For example, if
stableis declared at the API level, endpoints may bedraft,betaorstable, but ifbetais declared at the API level, endpoints may only bedraftorbeta.
- For example, if
-
Individual endpoints MUST NOT set deprecatedtofalse, if the API has setx-deprecatedtotrue. -
Individual endpoints MAY override x-sunsetto an earlier date than the API, but NOT later. -
You MAY use the property deprecatedon other components in the specification, such as on parameters, headers, schemas or schema properties. However, this should only be used to indicate that the component will be removed in the next version of the API. Removal of a deprecated component is still considered to be a breaking change.
3. Naming & Structure Conventions
3.1 Resource Naming
-
You SHOULD use plural Nouns - For example: /customers,/benefitsand/offers- Exception: Singleton resources that represent a unique entity MAY use singular form, such as
/user/profile,/me
- Exception: Singleton resources that represent a unique entity MAY use singular form, such as
-
Hierarchical Structure - For related resources you SHOULD use hierarchical URL structure. For example: /orders/{orderId}/fees -
No Actions in URL - Actions (such as create, delete) SHOULD be handled via the HTTP method, not in the URL itself -
URL in kebab-case - URL for APIs MUST be in kebab-case. For example /realtime-deviations/v1/subscription -
Field Names in camelCase - For request and response body field names and query parameters, camelCase MUST be used -
Consistent Terminology - Similar concepts across different APIs SHOULD use consistent naming. For example, don’t mix /usersand/peopleor/proposalsand/offerswhen referring to the same concept across different APIs. -
Server URL MUST be in lowercase -
Paths SHOULD NOT contain “api” in the name
3.2 Versioning
3.2.1 API Versioning
-
URL Based Versioning - You MUST include the version number in the URL -
Best Practices on Version Info - You SHOULD place the version number immediately after the domain, and before the resource path itself - Example:
https://api.entur.io/sales/v1/orders?distributionChannelId=1
- Example:
-
You SHOULD NOT include the stability level of the api in the URL. Instead, follow the lifecycle guidelines - Example:
https://api.entur.io/sales/v1beta/orders?distributionChannelId=1
- Example:
3.2.2 API Spec Versioning
API spec versioning is done automatically when publishing the spec. If the published specification has an Entur id that has been published before from the same repository, it will be considered a new version of the same spec. The version is set based on the CalVer versioning convention, and uses the format YYYY.MM.MICRO. When the spec is published, the current year and month is used, and MICRO is set to “00”. If a version already exists for the given year and month, “01” is used (and so on). If the published spec is equal to the current spec, no new version is created. Because versioning is done automatically, the value in info.version is ignored, but must be set in order for linting to pass, therefore a placeholder value like 1.0.0 may be used.
3.3 Tags Naming
Tags should be used as a logical grouping that reflects functional domains, use cases or data types, not internal architecture.
- Bad example:
internal,partner,production,route-service-v2,misc,other - Good example, when using tags for functional domains:
Journey Planning,Realtime Departures - Good example, when using tags for use-cases :
Search,Booking - Good example, when using tags for data types :
Routes,Departures
3.4 Enumerations
Prefer using open-ended values with examples, instead of enum
The enum (https://json-schema.org/understanding-json-schema/reference/enum) keyword in JSON schema (and therefore inherited to OpenAPI), indicates that a schema property will only have a defined, limited set of values. This is problematic for evolving a schema over time. Adding a new allowed value is considered a breaking change, as the previous contract is no longer valid. Because of this, you SHOULD NOT use the enum keyword, unless you are sure that there will never be a need for new values. Instead, declare your enumeration with an open-ended type, such as string, and use examples to indicate currently known values. For example:
transportMode:
type: string
examples:
- TRAIN
- BUS
This means that the consumer knows that the field transportMode can have either of those values, but that it may also return other values, so they are encouraged to implement a fallback. On the other side, the producer may add new entries to examples and start to return those values, without it being a breaking change.
4. Communication Standards
4.1 HTTP Status Codes
-
Request body is only allowed for PUT, POST and PATCH
| Code | Description | GET | POST | PUT | PATCH | DELETE |
|---|---|---|---|---|---|---|
| 200 | OK | |||||
| 201 | Created | |||||
| 202 | Accepted | |||||
| 204 | No Content | |||||
| 302 | Found | |||||
| 303 | See Other | |||||
| 400 | Bad Request | |||||
| 401 | Unauthorized | |||||
| 403 | Forbidden | |||||
| 404 | Not Found | |||||
| 409 | Conflict | |||||
| 410 | Gone | |||||
| 412 | Precondition Failed | |||||
| 500 | Internal Server Error | |||||
| 503 | Service Unavailable |
4.2 Error Handling
When an error occurs, the IETF standard for Problem Details for HTTP APIs (RFC 9457) SHOULD be followed, with few additional guidelines:
-
Error responses MUST either use media type application/problem+json, ORapplication/problem+xml(if Accept header isapplication/xml) -
The fields titleandstatusMUST be included -
The detailfield SHOULD be included when it provides additional useful information -
The typefield MAY be included. If included, it MUST be an absolute URI -
The instanceMAY be included. If included, it MUST be an absolute URI
Example:
HTTP/1.1 403 Forbidden Content-Type: application/problem+json Content-Language: en
{ "type": "https://example.com/request-endpoint", "title": "Access forbidden", "status": 403, "detail": "You do not have permission to access this resource.", "instance": "https://example.com/something/something", "balance": 30, //Custom field "recommended-action": "Something." //Custom field }
5. Data Formatting Standards
5.1 Language & Spelling
-
You MAY allow clients to specify a preferred language via the Accept-Languageheader or a query parameter. -
You MAY return the Content-Languageheader to inform clients of language used in response. -
Accept-LanguageandContent-Languagevalues MUST be valid IETF BCP 47 language tags. BCP 47 uses ISO 639-1 codes when available, and ISO 639-3 codes if no two-letter code exist. E.g. “en”, not “eng”. Also, as specified by BCP 47, clients may send multiple tags with quality values (e.g. nb,en;q=0.9). -
Macrolanguage tags (e.g., “no”) MUST NOT be used - instead use the specific language variant (e.g., “nb” or “nn”). -
British English spelling as defined in the Oxford English Dictionary SHOULD be used for consistency
Example:
GET /api/v1/info Accept-Language: nb
GET /api/v1/info?lang=nb
5.2 Date & Time
-
You SHOULD use the ISO 8601 standard for all date and timestamps
Example:
“lastUpdated”: “2025-02-13T15:30:00Z”
5.3 Currency Representation
-
Prices should be specified as floating-point numbers, represented as strings -
Request: Max 18 digits total; max 5 decimals (follows ISO 20022) -
Response: Always serialized with the standard number of decimals. For NOK, this means 2, since øre is the smallest unit.
Example:
{
"amount": "99.00"
"currency": "NOK"
}
5.4 Character Encoding
-
You MUST encode all text in UTF-8 -
Set the Content-Type header to for example application/json; charset=utf-8- Tip: test the api with international characters (e.g. æ, ø, å)
5.5 HTTP Headers
-
HTTP headers MUST use Hyphenated-Pascal-Case format (e.g., Content-Type,Accept-Language) -
Custom HTTP headers SHOULD use the prefix Entur-, with some exceptions.
5.5.1 Known Custom HTTP Headers
ET-Client-Name
-
All endpoints SHOULD allow consumers to identify themselves using the request header ET-Client-Name. The header is added automatically to all endpoints in a specification before publication to the developer portal.
Entur-POS
Used to declare which “point-of-sale” a request is coming from.
Entur-Distribution-Channel
Used to declare which distribution channel (sales channel) a request is coming from.
X-Correlation-Id
A “de-facto” standard for correlating a request throughout a microservice architecture. At Entur, this header is handled by our logging library cloud-logging. It is automatically added to all endpoints in a specification before publication to the developer portal.
6. Advanced Design Patterns
6.1 Pagination and Sorting
If you implement pagination, you MUST use one of these approaches:
- Offset pagination with query parameters
offsetandlimit:GET /api/v1/bus-stops?city=Oslo&offset=10&limit=20 - Cursor / Keyset Pagination with query parameters
cursorandpageSize:GET /api/v1/bus-stops?city=Oslo&cursor=eyJpZCI6MTAwfQ&pageSize=20
See pagination for more details.
All returned collections MUST be sorted, which it means that the order is deterministic: the same request MUST return items in the same order.
If you implement client-controlled sorting, you MUST use query parameter sort:
GET /api/v1/bus-stops?city=Oslo&sort=name,asc&sort=something,desc
See sorting for more details.
6.2 Partial Responses
-
You MAY let clients choose which fields to include to reduce data transfer
Example:
GET /api/v1/bus-stops?fields=id,name,location
6.3 Batch Operations
-
When multiple operations need to be handled in a single call, the API SHOULD support batch operations
Example:
POST /api/v1/batch
{
"operations": [
{ "method": "GET", "path": "/v1/resource/1" },
{ "method": "DELETE", "path": "/v1/resource/2" }
]
}
6.4 Caching & Resource Expiration
-
You MAY use HTTP headers such as Cache-Control, ETag, and Last-Modified
Example:
HTTP/1.1 200 OK
Cache-Control: max-age=3600
ETag: "abc123"
Last-Modified: Fri, 13 Feb 2025 15:30:00 UTC
The client can then cache the result for 3600 seconds, and after that it can issue a conditional GET using the ETag and Last-Modified headers
Example:
GET /your-resource HTTP/1.1
If-None-Match: "abc123"
If-Modified-Since: Fri, 13 Feb 2025 15:30:00 UTC
If both If-None-Match and If-Modified-Since are sent, the server MUST ignore If-Modified-Since (RFC7232 §3.3). If the resource has not changed, the server can respond with 304 Not Modified.
Example:
HTTP/1.1 304 Not Modified
6.5 Import & Export Formats
-
You SHOULD support JSON, unless you have a good reason not to. -
Use the HTTP Accept header to specify desired response format
Example:
GET /api/v1/data
Accept: application/json
6.6 Validation
-
You SHOULD validate all incoming data and return detailed error messages with appropriate HTTP status codes -
Error messages SHOULD be specific enough to guide the client toward fixing the issue -
Validation errors SHOULD return 400 Bad Request status code
Example:
HTTP/1.1 400 Bad Request Content-Type: application/problem+json Content-Language: en
{
"type": "https://api.entur.io/v1/orders",
"title": "Invalid input",
"status": 400,
"detail": "Field must be valid email.",
"errors": [
{
"field": "email",
"message": "Field must be valid email.",
"code": "INVALID_FORMAT"
}
]
}
6.7 HATEOAS
-
You MAY include links to related resources in responses to enable easy API navigation
Example:
{
"id": 123,
"name": "Sentralstasjonen",
"links": [
{ "rel": "self", "href": "/api/v1/bus-stops/123" },
{ "rel": "schedules", "href": "/api/v1/bus-stops/123/schedules" }
]
}
7. Alternative Approaches
7.1 When to Consider Non-RESTful Designs
There may be situations where a pure REST architecture is not the best solution. In such cases, consider alternative design patterns. The choice should be justified based on performance requirements, complexity, and consistency with other systems, as well as overall guidelines in the architect’s intent.
Alternative Design Patterns:
- GraphQL: For flexible queries where the client can specify exactly what data is needed
- Event-driven architecture: For asynchronous or event-based scenarios
- WebSocket: For real-time bidirectional communication
- gRPC: For high-performance, strongly-typed services
FAQ
Must existing APIs conform the guidelines?
- Non-breaking changes (like adding example values) SHOULD be updated to be compliant with the guidelines.
- Breaking changes MAY be added in a new version of the API.
- New APIs MUST follow the guidelines.
Appendix
I. Breaking changes
A breaking change in an API is a change that “breaks” a clients integration with the API, without the client having made any changes on their end. The nature of breaking changes vary depending on the type of integration. For REST APIs, it is the OpenAPI specification that specifies the contract with the client. In practice, this means that if a client fetches the OpenAPI specification for a service and integrates with it, changes to the service that invalidates that specification, is considered breaking. In other words, changes should be backwards compatible with regards to the specification.
Examples of breaking changes:
- Removal of a non-nullable field in a response (or making it nullable)
- Adding a new required field in a request (or making a nullable field required)
- Changing the physical datatype of a field, for example for string to number
- Removal of an endpoint without following the deprecation rules
- Adding new values to an
enumproperty in a response. Anenumis a contract specifying that a field will only have one of the following values. If the API starts to return new values, then the old specification is no longer valid, breaking backwards compatibility. See: 3.4 Enumerations.
However, an OpenAPI specification will never be a complete description of the behavior of an API. Some behavior will only be apparent when observing the output of the API, and clients will make assumptions based on the observed behavior. Examples:
- The order of items in an array. A client may observe that elements in an array are sorted alphabetically and, even though this is not specified, assume that it will continue to be. Changing the sort order may then break a clients integration, even though the contract is not technically broken.
- When exceptions are thrown. A client may observe that a given scenario returns an HTTP exception code, and assume that it will continue to do so, even though this is not explicitly documented in the specification.
These types of changes are harder to detect. They should be avoided, but sometimes clients may make unreasonable assumptions that can’t be taken into consideration. In the end, it is up to the API producer to judge whether a change should be considered breaking or not, and how to communicate the change to consumers. However, the amount of such breakages can be minimized through well-thought out and predictable API design that follows common guidelines.
II. Deprecation rules
At Entur, we should generally avoid making breaking changes. Instead, we should provide a deprecation period in which consumers of an API are given time to migrate to a new version. This period should be at least 12 months. The API producer is responsible for ensuring that their consumers are aware of the deprecation, but they can make this easier by following the guidelines for marking deprecation.