iStreamPlanet · API Governance Rules

iStreamPlanet API Rules

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

19 Rules error 14 warn 5
Published by iStreamPlanet Served by the provider at https://github.com/istreamlabs/rest-api-lint/blob/98553242c3d8d4e64c628aa53ce33f9d27dde10b/isp-rules.yaml; the copy below was fetched from there.
View Rules File View on GitHub

Rule Categories

delete disallow dns error headers json listing params patch post properties resource schema server

Rules

warn
english
$..description
error
json-responses
$..responses.[?(@property == '200' || @ == '201' || @ == '202' || @ == '400' || @ == '401' || @ == '403' || @ == '404' || @ == '422' || @ == '500')].content
error
patch-request-content-type
`PATCH` requests cannot use `application/json`
$.paths.*.[?(@property == 'patch')].requestBody.content[?(@property == 'application/json')]^
warn
patch-prefer-merge
Prefer `application/merge-patch+json` for `PATCH` requests
$.paths.*.[?(@property == 'patch')].requestBody.content
error
server-version
Server URL must include version in the path (except localhost)
$.servers.[*].url
warn
iso8601
$..parameters[?(@ != null)]
error
resource-nouns
$.paths.*~
error
dns-friendly
Identifier parameter missing DNS-friendly pattern, e.g. ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
$..parameters[?(@ != null && (@.name || '').toString().match(/^id[ -_A-Z]|[ -_]id$|[a-z0-9]Id$/) && (@.name || '').toString() !== 'upid_id')]
error
resource-schemas
$..['application/json']
warn
schema-descriptions
$..properties.*
error
properties-lower-snake-case
$..properties[?(!@property.toString().startsWith("$"))]
error
post-with-id
Use PUT/PATCH rather than POSTing with a user-supplied identifier
$.paths.[?(@property.toString().match(/[ -_]id}$|[a-z]Id}$/))]
warn
listing-returns-list
$.paths.[?(!@property.toString().includes("}"))].get.responses.[?(@property.toString().startsWith("2"))].content.*.schema
error
disallow-body
204 response should have no body. Use e.g. 200 otherwise.
$..responses.204
error
delete-response
Delete should return an HTTP 204
$.paths.*[?(@property === 'delete')].responses
error
error-detail
Errors must be problem+JSON or text/plain and include a "detail" field
$..responses.[?((@property.toString().startsWith("4") || @property.toString() === "500") && @property.toString() != "429")]
error
headers-hyphenated-pascal-case
'HTTP' headers MUST follow 'Hyphenated-Pascal-Case' notation
$..parameters[?(@ != null && @.in == 'header')].name
error
params-lower-snake-case
$..parameters[?(@ != null && (@.in == 'query' || @ == 'path'))].name
error
params-location
Required parameters must be in the URL path or header
$..parameters[?(@ != null && @.in != 'path' && @.in != 'header')]

Spectral Ruleset

Raw ↑
# harvested from https://github.com/istreamlabs/rest-api-lint/blob/98553242c3d8d4e64c628aa53ce33f9d27dde10b/isp-rules.yaml on 2026-10-09 — a Spectral ruleset published in the provider's own GitHub repository (istreamlabs/rest-api-lint); found by GitHub code search, fetched verbatim
x-method: harvested
x-stamped: 2026-10-09
x-source-url: https://github.com/istreamlabs/rest-api-lint/blob/98553242c3d8d4e64c628aa53ce33f9d27dde10b/isp-rules.yaml
formats:
  - "oas3"
extends: spectral:oas
functionsDir: isp-functions
functions:
  - contains
  - english
  - iso8601
  - noun
rules:
  # English recommendations for description fields
  english:
    severity: warn
    given: $..description
    then:
      function: english
  # Use JSON as much as possible
  json-responses:
    severity: error
    message: "{{description}}: {{error}}"
    given: $..responses.[?(@property == '200' || @ == '201' || @ == '202' || @ == '400' || @ == '401' || @ == '403' || @ == '404' || @ == '422' || @ == '500')].content
    then:
      function: contains
      functionOptions:
        match: "json"
  patch-request-content-type:
    severity: error
    description: "`PATCH` requests cannot use `application/json`"
    given: $.paths.*.[?(@property == 'patch')].requestBody.content[?(@property == 'application/json')]^
    then:
      function: falsy
  patch-prefer-merge:
    severity: warn
    description: Prefer `application/merge-patch+json` for `PATCH` requests
    given: $.paths.*.[?(@property == 'patch')].requestBody.content
    then:
      function: contains
      functionOptions:
        match: 'application/merge-patch\+json'
  # Reduce duplication in data structures
  # TODO... maybe detect similar strings in JSON paths?
  # Version the API
  server-version:
    severity: error
    description: Server URL must include version in the path (except localhost)
    given: $.servers.[*].url
    then:
      function: pattern
      functionOptions:
        match: "localhost|/v[0-9]+$"
  # Use ISO 8601 for dates
  iso8601:
    severity: warn
    given: $..parameters[?(@ != null)]
    then:
      function: iso8601
  # Auth? TODO
  # Resource plural nouns
  resource-nouns:
    severity: error
    given: $.paths.*~
    then:
      function: noun
  # DNS & URL friendliness
  dns-friendly:
    severity: error
    description: Identifier parameter missing DNS-friendly pattern, e.g. ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
    # 'upid_id' is specifically excluded since it is not an internally defined ID. It is the ID part of a UPID
    # which should allow any string value.
    given: $..parameters[?(@ != null && (@.name || '').toString().match(/^id[ -_A-Z]|[ -_]id$|[a-z0-9]Id$/) && (@.name || '').toString() !== 'upid_id')]
    then:
      # Currently this just checks for existence of any pattern, but it should
      # both catch small mistakes and not be too limited if params have
      # additional constraints (e.g. not limited to a specific pattern).
      field: schema.pattern
      function: truthy
  # Resources should have schemas with descriptions
  resource-schemas:
    severity: error
    given: $..['application/json']
    then:
      field: schema
      function: truthy
  schema-descriptions:
    severity: warn
    given: $..properties.*
    then:
      field: description
      function: truthy
  # Resource field casing
  properties-lower-snake-case:
    severity: error
    given: $..properties[?(!@property.toString().startsWith("$"))]
    then:
      function: casing
      functionOptions:
        type: snake
  # Don't POST with identifier
  post-with-id:
    severity: error
    description: Use PUT/PATCH rather than POSTing with a user-supplied identifier
    given: $.paths.[?(@property.toString().match(/[ -_]id}$|[a-z]Id}$/))]
    then:
      field: post
      function: falsy
  # Listing should return a list & include pagination
  listing-returns-list:
    severity: warn
    message: Type "{{value}}" should be "array" when returning a list of resources
    given: $.paths.[?(!@property.toString().includes("}"))].get.responses.[?(@property.toString().startsWith("2"))].content.*.schema
    then:
      field: type
      function: enumeration
      functionOptions:
        values:
          - array
  # Create/update should either repond 204 or with a JSON body
  disallow-body:
    severity: error
    description: 204 response should have no body. Use e.g. 200 otherwise.
    given: $..responses.204
    then:
      field: content
      function: falsy
  # requiring a body fails for image/json and cannot because because of a spectral
  # bug that prevents image/jpeg with type:"string" from parsing.
  # TODO: Readd this if spectral ever fixes that bug.
  # require-body:
  #   severity: error
  #   description: 200 response must have a body. Use 201/204 otherwise.
  #   given: $..responses.200
  #   then:
  #     field: content
  #     function: truthy
  delete-response:
    severity: error
    description: Delete should return an HTTP 204
    given: $.paths.*[?(@property === 'delete')].responses
    then:
      field: "204"
      function: truthy
  # Errors must include a `detail` field
  error-detail:
    severity: error
    # 429 returns from the API Gateway, so we exclude it
    description: Errors must be problem+JSON or text/plain and include a "detail" field
    given: $..responses.[?((@property.toString().startsWith("4") || @property.toString() === "500") && @property.toString() != "429")]
    then:
      - field: content
        function: truthy
      - field: content.application/problem+json.schema
        function: truthy
      - field: content.application/problem+json.schema.properties.detail
        function: truthy
  # Header & parameter casing
  headers-hyphenated-pascal-case:
    severity: error
    given: "$..parameters[?(@ != null && @.in == 'header')].name"
    description: "'HTTP' headers MUST follow 'Hyphenated-Pascal-Case' notation"
    then:
      function: pattern
      functionOptions:
        match: "/^([A-Z][a-z0-9]-)*([A-Z][a-z0-9])+/"
  params-lower-snake-case:
    severity: error
    message: "`{{value}}` must follow `snake` notation"
    given: "$..parameters[?(@ != null && (@.in == 'query' || @ == 'path'))].name"
    then:
      function: casing
      functionOptions:
        type: snake
  # Parameter location constraints
  params-location:
    severity: error
    description: Required parameters must be in the URL path or header
    given: $..parameters[?(@ != null && @.in != 'path' && @.in != 'header')]
    then:
      field: required
      function: falsy
  # Date/time ranges
  # TODO

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/istreamplanet-rest-api-lint-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.