# harvested from https://github.com/entur/api-guidelines/blob/f92bec1065e08092b8c5ee62677ff9345ed1a174/.spectral.yml on 2026-10-09 — a Spectral ruleset published in the provider's own GitHub repository (entur/api-guidelines); found by GitHub code search, fetched verbatim
x-method: harvested
x-stamped: 2026-10-09
x-source-url: https://github.com/entur/api-guidelines/blob/f92bec1065e08092b8c5ee62677ff9345ed1a174/.spectral.yml
# API Guidelines Ruleset
# This ruleset enforces the API design standards described in our API Guidelines document.
# Structure follows the same organization as the main guidelines document for easy reference.
# OpenAPI Specification version 3.x
extends: [spectral:oas]
functions:
- date
- conditionallyDefined
- requireExampleOrRef
- requireRequestBodyDescription
- xEnturPermissions
rules:
# =============================================================================
# 1. Introduction - Not lintable
# =============================================================================
# =============================================================================
# 2. Core Principles
# =============================================================================
# -------------------------------------------------------------------------
# 2.1 General Design Principles
# -------------------------------------------------------------------------
entur-info-title:
message: "The OpenAPI info section MUST include a non-empty \"title\"."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: error
given: $
then:
field: info.title
function: truthy
entur-info-title-no-api:
message: "API titles SHOULD NOT contain the word 'api'."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: warn
given: $.info.title
then:
function: pattern
functionOptions:
notMatch: "/\\bapi\\b/i"
info-description: error
info-contact: off
# HTTP Methods
entur-operation-standard-methods:
message: "Operations SHOULD use standard HTTP methods (`get`, `post`, `put`, `patch`, `delete`). Invalid operation: {{property}}."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
given: $.paths[*]
severity: warn
then:
field: "@key"
function: pattern
functionOptions:
notMatch: "^(options|head|trace)$"
# Documentation with examples
entur-example-parameter:
message: "Parameters SHOULD have example values."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: warn
recommended: false
given: $.paths.*.*.parameters.*
then:
field: example
function: defined
entur-parameter-description:
message: "Parameters SHOULD have a description."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: warn
given: $.paths.*.*.parameters.*
then:
field: description
function: truthy
entur-example-schema-property:
message: "Properties in components schema SHOULD have example values."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: warn
recommended: false
#For schema properties where type is not array, or items is not a ref. (Array with ref to other schema does not need an example)
given: $.components.schemas.*.properties[?(@.type != 'array' || !@.items.$ref)]
then:
field: example
function: defined
entur-request-body-examples:
message: "Request bodies SHOULD include at least one example."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: warn
given: $.paths.*.*.requestBody.content.*
then:
function: requireExampleOrRef
entur-request-body-description:
message: "Request bodies SHOULD have a description, either directly or on the referenced schema."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: warn
given: $.paths.*.*.requestBody
then:
function: requireRequestBodyDescription
entur-response-body-examples:
message: "Response bodies SHOULD include at least one example."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: warn
given: $.paths.*.*.responses.*.content.*
then:
function: requireExampleOrRef
entur-operation-summary:
message: "Operations SHOULD have a non-empty summary field."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: warn
given: $.paths.*[get,post,put,patch,delete,options,head,trace]
then:
field: summary
function: truthy
# openapi spec version 3
entur-openapi-version-3:
message: "OpenAPI specification must use version 3.x"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: error
given: "$"
then:
- field: openapi
function: pattern
functionOptions:
match: "^3\\.\\d+\\.\\d+$"
- field: swagger
function: falsy
# Security - HTTPS requirement
entur-hosts-https-only:
message: "Servers MUST use HTTPS. Invalid URL: {{value}}"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: error
given: $.servers[*].url
then:
function: pattern
functionOptions:
match: ^(https:)
entur-hosts-not-localhost:
message: "Server URLs SHOULD NOT use localhost or 127.0.0.1 as hostname. Invalid URL: {{value}}"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#21-general-design-principles"
severity: warn
given: $.servers[*].url
then:
function: pattern
functionOptions:
notMatch: "https?://(localhost|127\\.0\\.0\\.1)(/|$)"
invert: true
# -------------------------------------------------------------------------
# 2.3 Authentication and authorization
# -------------------------------------------------------------------------
entur-permissions:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#233-documenting-permissions-for-partner-endpoints"
severity: error
given: $.paths.*[get,post,put,patch,delete,options,head,trace].x-entur-permissions
then:
function: xEnturPermissions
# -------------------------------------------------------------------------
# 2.4 Entur Metadata
# -------------------------------------------------------------------------
entur-info-metadata-id:
message: "The OpenAPI info section MUST include \"x-entur-metadata.id\"."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#241-identifying-a-specification"
severity: error
given: $
then:
field: info.x-entur-metadata.id
function: truthy
entur-info-metadata-id-kebab-case:
message: "The \"x-entur-metadata.id\" MUST be in lower-kebab-case format. Invalid value: {{value}}"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#241-identifying-a-specification"
severity: error
given: $.info.x-entur-metadata.id
then:
function: pattern
functionOptions:
match: ^[a-z0-9]+(-[a-z0-9]+)*$
entur-info-metadata-audience:
message: "The OpenAPI info section MUST include \"x-entur-metadata.audience\"."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#24-entur-metadata"
severity: error
given: $
then:
field: info.x-entur-metadata.audience
function: truthy
entur-info-metadata-audience-valid:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#24-entur-metadata"
severity: error
given: $.info.x-entur-metadata.audience
then:
function: enumeration
functionOptions:
values:
- open
- partner
- internal
- private
entur-info-metadata-owner:
message: "The OpenAPI info section MUST include \"x-entur-metadata.owner\"."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#242-specification-owner"
severity: error
given: $
then:
field: info.x-entur-metadata.owner
function: truthy
entur-info-metadata-owner-valid:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#242-specification-owner"
severity: error
given: $.info.x-entur-metadata.owner
then:
function: pattern
functionOptions:
match: ^team-[a-z0-9]+(-[a-z0-9]+)*$
entur-info-metadata-parent-id-kebab-case:
message: "The \"x-entur-metadata.parentId\" MUST be in lower-kebab-case format. Invalid value: {{value}}"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#243-merging-specifications"
severity: error
given: $.info.x-entur-metadata.parentId
then:
function: pattern
functionOptions:
match: ^[a-z0-9]+(-[a-z0-9]+)*$
entur-info-metadata-devExtensions:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#244-development-only-openapi-extensions"
severity: error
given: $.info.x-entur-metadata.devExtensions
then:
function: schema
functionOptions:
schema:
type: array
items:
type: string
pattern: ^x-.*$
# -------------------------------------------------------------------------
# 2.5 Lifecycle
# -------------------------------------------------------------------------
## On API level
entur-stability-level-api:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#25-lifecycle"
severity: error
given: $.info.x-stability-level
then:
function: enumeration
functionOptions:
values: [draft, beta, stable]
entur-deprecation-api:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#254-deprecation"
severity: error
given: $.info.x-deprecated
then:
function: schema
functionOptions:
schema:
type: boolean
entur-sunset-api:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#254-deprecation"
severity: error
given: $.info
then:
function: conditionallyDefined
functionOptions:
field: x-sunset
conditionalField: x-deprecated
havingValue: true
entur-sunset-format-api:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#254-deprecation"
severity: error
given: $.info.x-sunset
then:
function: date
# On individual operation level
entur-stability-level-operation:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#25-lifecycle"
severity: error
given: $.paths.*[get,post,put,patch,delete,options,head,trace].x-stability-level
then:
function: enumeration
functionOptions:
values: [draft, beta, stable]
entur-sunset-operation:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#254-deprecation"
severity: info # This one should be an error, but for an introduction period, make it just info.
given: $.paths.*[get,post,put,patch,delete,options,head,trace]
then:
function: conditionallyDefined
functionOptions:
field: x-sunset
conditionalField: deprecated
havingValue: true
entur-sunset-format-operation:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#254-deprecation"
severity: error
given: $.paths.*[get,post,put,patch,delete,options,head,trace].x-sunset
then:
function: date
# =============================================================================
# 3. Naming & Structure Conventions
# =============================================================================
# -------------------------------------------------------------------------
# 3.1 Resource Naming
# -------------------------------------------------------------------------
# URL format requirements
entur-paths-format:
message: "Paths MUST be in kebab-case (lower case and separated with hyphens), with single slashes, and no trailing slash at end of path. Invalid path: {{property}}."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#31-resource-naming"
severity: error
given: $.paths.*~
then:
function: pattern
functionOptions:
#Match leading slash followed by kebab casing, and then optional trailing kebab with url params allowed. No trailing slash.
#Double slashes now allowed.
#Custom functions not allowed in path for now (e.g. /ecards/{mediaSerialNumberId}:block)
match: ^(\/[a-z0-9]+(-[a-z0-9]+)*)(\/[a-z0-9]+(-[a-z0-9]+)*|\/{.+})*$
# Field naming conventions
entur-query-parameters-lower-camel-case:
message: "Query parameter names MUST be lowerCamelCase. Invalid name: {{value}}"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#31-resource-naming"
severity: error
given: $.paths.*.*.parameters[?(@.in=='query')].name
then:
function: pattern
functionOptions:
match: ^[a-z][a-zA-Z0-9]*$
entur-path-parameters-camelCase-alphanumeric:
message: "Path parameter names MUST be lowerCamelCase. Invalid name: {{value}}"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#31-resource-naming"
severity: error
given: $..parameters[?(@.in == 'path')].name
then:
function: pattern
functionOptions:
match: ^[a-z][a-zA-Z0-9]*$
entur-body-fields-lower-camel-case:
message: "Request and Response body field names MUST be lowerCamelCase."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#31-resource-naming"
severity: error
given: $..[?(@property === 'properties')]
then:
field: "@key"
function: pattern
functionOptions:
match: ^[a-z][a-zA-Z0-9]*$
# Server URL case requirements
entur-server-urls-lowercase:
message: "Server URLs MUST be in lowercase. Invalid URL: {{value}}"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#31-resource-naming"
severity: error
given: $.servers[*].url
then:
function: pattern
functionOptions:
match: ^[^A-Z]*$
# Avoid 'api' in paths
entur-paths-with-api:
message: "Paths SHOULD NOT contain 'api'."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#31-resource-naming"
severity: warn
given: $.paths.*~
then:
function: pattern
functionOptions:
notMatch: "^(?!\/api-docs$).*\\bapi\\b.*$"
# -------------------------------------------------------------------------
# 3.2 Versioning
# -------------------------------------------------------------------------
# =============================================================================
# 4. Communication Standards
# =============================================================================
# -------------------------------------------------------------------------
# 4.1 HTTP Status Codes
# -------------------------------------------------------------------------
# Request body allowed methods
entur-request-body-allowed-methods:
message: "Request body is allowed only for PUT, POST, and PATCH."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#41-http-status-codes"
severity: error
given:
- "$.paths[*].get.requestBody"
- "$.paths[*].delete.requestBody"
- "$.paths[*].options.requestBody"
- "$.paths[*].head.requestBody"
- "$.paths[*].trace.requestBody"
then:
function: falsy
# HTTP method responses validation
entur-get-responses-validation:
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#41-http-status-codes"
severity: error
given: $.paths.*.get.responses
then:
field: "@key"
function: enumeration
functionOptions:
values: ["200", "302", "304", "400", "401", "403", "404", "500", "503", "default"]
entur-delete-responses-validation:
message: "Invalid response code: {{value}}. DELETE responses MUST use one of these response codes: 200, 204, 400, 401, 403, 404, 409, 500, 503"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#41-http-status-codes"
severity: error
given: $.paths.*.delete.responses
then:
field: "@key"
function: enumeration
functionOptions:
values: ["200", "204", "400", "401", "403", "404", "409", "500", "503", "default"]
entur-post-responses-validation:
message: "Invalid response code: {{value}}. POST responses MUST use one of these response codes: 200, 201, 202, 204, 303, 400, 401, 403, 404, 409, 500, 503"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#41-http-status-codes"
severity: error
given: $.paths.*.post.responses
then:
field: "@key"
function: enumeration
functionOptions:
values: ["200", "201", "202", "204", "303", "400", "401", "403", "404", "409", "500", "503", "default"]
entur-put-responses-validation:
message: "Invalid response code: {{value}}. PUT responses MUST use one of these response codes: 200, 204, 400, 401, 403, 404, 409, 500, 503"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#41-http-status-codes"
severity: error
given: $.paths.*.put.responses
then:
field: "@key"
function: enumeration
functionOptions:
values: ["200", "201", "204", "400", "401", "403", "404", "409", "500", "503", "default"]
entur-patch-responses-validation:
message: "Invalid response code: {{value}}. PATCH responses MUST use one of these response codes: 200, 204, 400, 401, 403, 404, 409, 500, 503"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#41-http-status-codes"
severity: error
given: $.paths.*.patch.responses
then:
field: "@key"
function: enumeration
functionOptions:
values: ["200", "204", "400", "401", "403", "404", "409", "500", "503", "default"]
# -------------------------------------------------------------------------
# 4.2 Error Handling - RFC 9457 compliance
# -------------------------------------------------------------------------
# Error response format validation
entur-rfc-9457-content-type:
message: "Error responses MUST have content type application/problem+json or application/problem+xml"
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#42-error-handling"
severity: warn
given: $.paths.*.*.responses[?(@property.match(/^(4|5)/))]
then:
- field: content
function: truthy
- field: content
function: schema
functionOptions:
# JSON Schema to require either the JSON or XML problem media-type
schema:
type: object
anyOf:
- required: ["application/problem+json"]
- required: ["application/problem+xml"]
entur-rfc-9457-body-title:
message: "Error responses MUST have property 'title'."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#42-error-handling"
severity: error
given: $.paths.*.*.responses[?(@property.match(/^(4|5)/))].content.*.schema.properties
then:
field: title
function: defined
entur-rfc-9457-body-status:
message: "Error responses MUST have property 'status'."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#42-error-handling"
severity: error
given: $.paths.*.*.responses[?(@property.match(/^(4|5)/))].content.*.schema.properties
then:
field: status
function: defined
entur-rfc-9457-body-detail:
message: "Error responses SHOULD have property 'detail' to provide additional context."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#42-error-handling"
severity: warn
given: $.paths.*.*.responses[?(@property.match(/^(4|5)/))].content.*.schema.properties
then:
field: detail
function: defined
# =============================================================================
# 5. Data Formatting Standards
# =============================================================================
# -------------------------------------------------------------------------
# 5.1 Language & Spelling
# -------------------------------------------------------------------------
entur-language-headers:
message: "Accept-Language and Content-Language should follow IETF BCP 47. And macrolanguages like 'no' should not be used - use 'nb' or 'nn'."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#51-language--spelling"
severity: error
given:
- $..parameters[?(@.in=='header' && @.name=='Accept-Language')].example
- $..parameters[?(@.in=='header' && @.name=='Accept-Language')].schema.example
- $..parameters[?(@.in=='header' && @.name=='Accept-Language')].schema.default
- $..parameters[?(@.in=='header' && @.name=='Content-Language')].example
- $..parameters[?(@.in=='header' && @.name=='Content-Language')].schema.example
- $..parameters[?(@.in=='header' && @.name=='Content-Language')].schema.default
then:
function: pattern
functionOptions:
notMatch: "^(nob|nno|eng|nor|no)\\b"
# -------------------------------------------------------------------------
# 5.2 Date & Time - Requires runtime validation
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# 5.3 Currency Representation - Requires runtime validation
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# 5.4 Character Encoding - Not directly lintable for UTF-8
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# 5.5 HTTP Headers
# -------------------------------------------------------------------------
# ET-Client-Name header not necessary
entur-not-et-client-name-header:
message: "Declaring header \"ET-Client-Name\" is not necessary."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#55-http-headers"
severity: warn
given: "$.paths[*]..parameters[?(@.in == 'header' && @.name == 'ET-Client-Name')].name"
then:
function: falsy
# Header naming conventions
entur-headers-hyphenated-pascal-case:
message: "HTTP header names MUST be in Hyphenated-Pascal-Case. Invalid name: \"{{value}}\"."
documentationUrl: "https://github.com/entur/api-guidelines/blob/main/guidelines.md#55-http-headers"
severity: error
given: "$..parameters[?(@.in == 'header' && @.name != 'ET-Client-Name' && @.name != 'Entur-POS')].name"
then:
function: pattern
functionOptions:
match: ^([A-Z][a-z0-9]*)(-[A-Z][a-z0-9]*)*$
# =============================================================================
# 6. Advanced Design Patterns
# =============================================================================
# Most advanced design patterns require runtime validation or manual review
# The rules here focus on aspects that can be statically verified
# -------------------------------------------------------------------------
# 6.5 Import & Export Formats - Accept header validation handled at runtime
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# 6.6 Validation - Error response format covered in section 4.2
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# 6.7 HATEOAS - Not directly lintable, requires manual review
# -------------------------------------------------------------------------
Every ruleset here is available over the APIs.io API and to AI agents over MCP.