Logius · API Governance Rules

Logius API Rules

Spectral linting rules defining API design standards and conventions for Logius.

23 Rules error 20 warn 3
View Rules File View on GitHub

Rule Categories

info nlgov:date nlgov:http nlgov:include nlgov:info nlgov:missing nlgov:openapi nlgov:paths nlgov:problem nlgov:property nlgov:query nlgov:schema nlgov:servers nlgov:specify nlgov:time nlgov:use

Rules

error
nlgov:openapi3
$.['openapi']
error
nlgov:openapi-root-exists
$
error
nlgov:missing-version-header
$..[responses][?(@property && @property.match(/(2|3)\d\d/))][headers]
error
nlgov:missing-header
$..[responses][?(@property && @property.match(/(2|3)\d\d/))]
error
nlgov:include-major-version-in-uri
$.servers[*]
error
nlgov:paths-no-trailing-slash
$.paths
error
info-contact
$
error
nlgov:info-contact-fields-exist
$.info.contact
error
nlgov:http-methods
$.paths[?(@property && @property.match(/(description|summary)/i))]
error
nlgov:paths-kebab-case
$.paths[?(@property && !@property.match(/\/openapi\.json/))]~
error
nlgov:query-keys-camel-case
$.paths.*.*.parameters[?(@.in=='query')]$.components.securitySchemes[?(@.in=='query')]
warn
nlgov:schema-camel-case
$.components.schemas[*]~
warn
nlgov:servers-use-https
$.servers[*]$.paths..servers[*]
error
nlgov:use-problem-schema
$..[responses][?(@property && @property.match(/(4|5)\d\d/))].content
error
nlgov:problem-schema-members
$..[responses][?(@property && @property.match(/(4|5)\d\d/))].content[?(@property=="application/problem+json" || @property=="application/problem+xml")]..schema
error
nlgov:problem-invalid-input
$.paths..[?( @property.match(/(get)|(delete)/) && @.parameters && @.parameters.length > 0 )]$.paths..[?( @property.match(/(put)|(post)|(patch)/))]
error
nlgov:problem-schema-members-bad-request
$..[responses][?(@property && @property.match(400))].content[?(@property=="application/problem+json" || @property=="application/problem+xml")]..schema.properties
warn
nlgov:property-casing
$.*.schemas[*].properties.[?(@property && @property.match(/_links/i))]
error
nlgov:date-time-ensure-timezone
$..properties[*].format
error
nlgov:time-without-timezone
$..properties[*].format
error
nlgov:specify-format-for-date-and-time
$..properties[date,datum]$..properties[?(@property && @property.match(/((\w+D)|(_[dD]))((ate)|(atum))/))]
error
nlgov:use-date-instead-of-datetime
$..properties[date,datum]..format$..properties[?(@property && @property.match(/((\w+D)|(_[dD]))((ate)|(atum))/))]..format
error
nlgov:semver
$.info.version

Spectral Ruleset

Raw ↑
# harvested from https://github.com/Logius-standaarden/Publicatie-Preview/blob/69a551bca14beeea84213c155a7c82c75bafabe5/API-Design-Rules/bad-request-errors/media/linter.yaml on 2026-10-09 — a Spectral ruleset published in the provider's own GitHub repository (Logius-standaarden/Publicatie-Preview); found by GitHub code search, fetched verbatim
x-method: harvested
x-stamped: 2026-10-09
x-source-url: https://github.com/Logius-standaarden/Publicatie-Preview/blob/69a551bca14beeea84213c155a7c82c75bafabe5/API-Design-Rules/bad-request-errors/media/linter.yaml
# Deze linter configuratie is geschreven voor Spectral. Voor meer informatie over
# die tool, zie https://stoplight.io/open-source/spectral
#
# De linter configuratie wordt tevens gehost op https://developer.overheid.nl (DON).
# Deze kan worden gebruikt in onder andere CI systemen voor live updates van de
# configuratie.
#
# Voor meer informatie hierover, zie de kennisbank van DON:
# https://developer.overheid.nl/kennisbank/apis/api-design-rules/api-design-rules-linter
#
# Hierbij ook de ingevoegde instructies die kunnen worden gekopieerd om de linter
# te draaien:
#
# ```
# npm install -g @stoplight/spectral-cli
# curl -L https://static.developer.overheid.nl/adr/ruleset.yaml > .linter.yaml
# spectral lint -r .linter.yaml $OAS_URL_OR_FILE
# ```

extends: spectral:oas

rules:
  oas3-api-servers: error

  #/core/doc-openapi
  nlgov:openapi3:
    severity: error
    given: $.['openapi']
    then:
      function: pattern
      functionOptions:
        match: '^3(.\d+){1,2}$'
    message: "The OpenAPI Specification is versioned using a `major.minor.patch` versioning scheme. Use a version 3 OpenAPI Specification for documentation."

  nlgov:openapi-root-exists:
    severity: error
    given: $
    then:
      field: openapi
      function: truthy
    message: "The root of the document must contain the `openapi` property."

  #/core/version-header
  nlgov:missing-version-header:
    severity: error
    given: $..[responses][?(@property && @property.match(/(2|3)\d\d/))][headers]
    then:
      function: or
      functionOptions:
        properties:
          - API-Version
          - Api-Version
          - Api-version
          - api-version
          - API-version
    message: "Return the full version number in a response header."
    documentationUrl: "https://developer.overheid.nl/kennisbank/apis/api-design-rules/hoe-te-voldoen/version-header"

  nlgov:missing-header:
    severity: error
    given: $..[responses][?(@property && @property.match(/(2|3)\d\d/))]
    then:
      field: headers
      function: truthy
    message: "Return the full version number in a response header."
    documentationUrl: "https://developer.overheid.nl/kennisbank/apis/api-design-rules/hoe-te-voldoen/version-header"

  #/core/uri-version
  nlgov:include-major-version-in-uri:
    severity: error
    given:
      - "$.servers[*]"
    then:
      function: pattern
      functionOptions:
        match: "\\/v[\\d+]"
      field: url
    message: "Include the major version number in the URI."
    documentationUrl: "https://developer.overheid.nl/kennisbank/apis/api-design-rules/hoe-te-voldoen/uri-version"

  #/core/no-trailing-slash
  nlgov:paths-no-trailing-slash:
    severity: error
    given:
      - "$.paths"
    then:
      function: pattern
      functionOptions:
        notMatch: ".+ \\/$"
      field: "@key"
    message: "Leave off trailing slashes from URIs."
    documentationUrl: "https://developer.overheid.nl/kennisbank/apis/api-design-rules/hoe-te-voldoen/no-trailing-slash"

  #/core/doc-openapi-contact
  info-contact:
    severity: error
    given:
      - "$"
    then:
      field: "info.contact"
      function: truthy
    message: 'Info object must have "contact" object.'
    documentationUrl: "https://developer.overheid.nl/kennisbank/apis/api-design-rules/hoe-te-voldoen/doc-openapi-contact"

  nlgov:info-contact-fields-exist:
    severity: error
    given:
      - "$.info.contact"
    then:
      function: schema
      functionOptions:
        schema:
          required:
            - email
            - name
            - url
    message: "Missing fields in `info.contact` field. Must specify email, name and url."
    documentationUrl: "https://developer.overheid.nl/kennisbank/apis/api-design-rules/hoe-te-voldoen/doc-openapi-contact"

  #/core/http-methods
  nlgov:http-methods:
    severity: error
    given:
      - "$.paths[?(@property && @property.match(/(description|summary)/i))]"
    then:
      function: pattern
      functionOptions:
        match: "post|put|get|delete|patch|parameters"
      field: "@key"
    message: "Only apply standard HTTP methods."
    documentationUrl: "https://developer.overheid.nl/kennisbank/apis/api-design-rules/hoe-te-voldoen/http-methods"

  #/core/path-segments-kebab-case
  nlgov:paths-kebab-case:
    severity: error
    message: "{{property}} is not kebab-case."
    given: $.paths[?(@property && !@property.match(/\/openapi\.json/))]~
    then:
      function: pattern
      functionOptions:
        # Deze regex bestaat uit meerdere delen. Ter toelichting:
        # - `\/` staat toe dat een pad enkel een `/` is (de landingspagina)
        # - `\/_[a-z0-9]+` staat toe dat het laatste stuk van een pad mag beginnen met een `_`
        # - ([a-z0-9\-]+|{[^}]+}) zijn kebab-case paden of met variabele notatie (`{id}`)
        # - Paden mogen nesten, waardoor de twee groep genest wordt, gescheiden met een `/`
        # - Een pad mag eindigen met een `/`. Dat is volgens een andere regel niet toegestaan, maar we willen niet twee errors genereren
        match: ^(\/|(\/_[a-z0-9]+|\/(([a-z0-9\-]+|{[^}]+})(\/([a-z0-9\-\.]+|{[^}]+}))*)(\/_[a-z]+)?)\/?)$

  #/core/query-keys-camel-case
  nlgov:query-keys-camel-case:
    severity: error
    message: "{{value}} is not lower camelCase."
    given:
      - $.paths.*.*.parameters[?(@.in=='query')]
      - $.components.securitySchemes[?(@.in=='query')]
    then:
      function: pattern
      field: name
      functionOptions:
        match: ^\$?[a-z][a-z\d]*([A-Z][a-z\d]*)*$

  nlgov:schema-camel-case:
    severity: warn
    message: "Schema name should be UpperCamelCase in {{path}}"
    given: >-
      $.components.schemas[*]~
    then:
      function: casing
      functionOptions:
        type: pascal
        separator:
          char: ""

  nlgov:servers-use-https:
    severity: warn
    message: "Server URL {{value}} {{error}}."
    given:
      - $.servers[*]
      - $.paths..servers[*]
    then:
      field: url
      function: pattern
      functionOptions:
        match: ^https://.*

  #/core/error-handling/problem-details
  nlgov:use-problem-schema:
    severity: error
    message: "The content type of an error response should be application/problem+json or application/problem+xml to match RFC 9457."
    given: $..[responses][?(@property && @property.match(/(4|5)\d\d/))].content
    then:
      function: schema
      functionOptions:
        schema:
          anyOf:
            - required: ["application/problem+json"]
            - required: ["application/problem+xml"]

  nlgov:problem-schema-members:
    severity: error
    message: "{{error}}. These fields are required: status, title and detail."
    given: $..[responses][?(@property && @property.match(/(4|5)\d\d/))].content[?(@property=="application/problem+json" || @property=="application/problem+xml")]..schema
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          properties:
            properties:
              type: object
              required:
              - status
              - title
              - detail

  #/core/error-handling/invalid-input
  nlgov:problem-invalid-input:
    severity: error
    message: "GET and DELETE endpoints that have parameters, and all other endpoints must be able to return a 400 response"
    given:
      - $.paths..[?( @property.match(/(get)|(delete)/) && @.parameters && @.parameters.length > 0 )]
      - $.paths..[?( @property.match(/(put)|(post)|(patch)/))]
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          properties:
            responses:
              type: object
              required:
              - "400"

  #/core/error-handling/bad-request
  nlgov:problem-schema-members-bad-request:
    severity: error
    given: $..[responses][?(@property && @property.match(400))].content[?(@property=="application/problem+json" || @property=="application/problem+xml")]..schema.properties
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          properties:
            errors:
              type: object
              properties:
                items:
                  type: object
                  properties:
                    properties:
                      type: object
                      properties:
                        in:
                          type: object
                          properties:
                            type:
                              const: string
                        detail:
                          type: object
                          properties:
                            type:
                              const: string
                        location:
                          type: object
                          properties:
                            type:
                              const: string
                        code:
                          type: object
                          properties:
                            type:
                              const: string
                        index:
                          type: object
                          properties:
                            type:
                              oneOf:
                              - const: number
                              - const: integer
                      required:
                      - in
                      - detail
                      - location
                      - code
                      - index
                  required:
                  - properties
              required:
              - items
          required:
          - errors

  nlgov:property-casing:
    severity: warn
    given:
      - "$.*.schemas[*].properties.[?(@property && @property.match(/_links/i))]"
    then:
      function: casing
      functionOptions:
        type: camel
      field: "@key"
    message: Properties must be lowerCamelCase.

  #/core/date-time/timezone
  nlgov:date-time-ensure-timezone:
    severity: error
    given: $..properties[*].format
    message: "Use date-time format which includes a timezone"
    then:
      function: pattern
      functionOptions:
        notMatch: "/^date-time-local$/"

  nlgov:time-without-timezone:
    severity: error
    given: $..properties[*].format
    message: "Use time-local format without a timezone"
    then:
      function: pattern
      functionOptions:
        notMatch: "/^time$/"

  #/core/date-time/date-omit-time-portion
  nlgov:specify-format-for-date-and-time:
    severity: error
    given:
      - $..properties[date,datum]
      - $..properties[?(@property && @property.match(/((\w+D)|(_[dD]))((ate)|(atum))/))]
    message: "Any date field must set 'format' to 'date'"
    then:
      function: schema
      functionOptions:
        schema:
          anyOf:
            - required: ["format"]
            - properties:
                allOf:
                  type: array
                  items:
                    required: ["format"]
              required: ["allOf"]

  nlgov:use-date-instead-of-datetime:
    severity: error
    given:
      - $..properties[date,datum]..format
      - $..properties[?(@property && @property.match(/((\w+D)|(_[dD]))((ate)|(atum))/))]..format
    message: "Field represents a date and therefore must set 'format' to 'date'"
    then:
      function: pattern
      functionOptions:
        notMatch: "/^date-time$/"

  nlgov:semver:
    severity: error
    message: "Version {{value}} is not in semver format."
    given: $.info.version
    then:
      function: pattern
      functionOptions:
        match: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

Work with this as data

Every ruleset here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for spectral rules

4 MCP tools reach this
  • find_rulesBrowse and filter every ruleset in the catalog.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This ruleset
curl "https://apis.io/api/v1/rules/logius-publicatie-preview-spectral-rules"
All spectral rules
curl "https://apis.io/api/v1/rules?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.