ipify · API Governance Rules

ipify API Rules

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

37 Rules error 15 warn 17 info 5
View Rules File View on GitHub

Rule Categories

deprecation example get info ipify openapi operation parameter paths response schema security servers tag tags

Rules

error
info-title-required
Info object must define a title.
$.info
warn
info-title-prefix-ipify
Spec title should start with `ipify` for consistent provider branding.
$.info.title
error
info-description-required
Info object must include a description of the API.
$.info
warn
info-contact-required
Info object should include a contact for the provider.
$.info
warn
info-license-required
Info object should declare a license (MIT for the free API, Commercial for Geolocation).
$.info
error
openapi-version-3
All ipify specs must declare OpenAPI 3.x.
$.openapi
error
servers-required
A `servers` array must be defined at the document root.
$
error
servers-must-be-https
ipify endpoints are HTTPS-only. No `http://` server URLs allowed.
$.servers[*].url
warn
servers-description-required
Each server entry must explain which surface it points to (IPv4, IPv6, dual-stack, account utility, etc.).
$.servers[*]
error
paths-no-trailing-slash
Paths must not end with a trailing slash (except the root `/`).
$.paths
error
paths-no-query-strings
Paths must not contain query strings — express them as parameters.
$.paths
warn
paths-lowercase-segments
Path segments must be lowercase kebab-case (matches ipify's `/country`, `/country,city`, `/service/account-balance`).
$.paths
error
operation-operationId-required
Every operation must declare an `operationId`.
$.paths[*][get,post,put,patch,delete]
warn
operation-operationId-camelcase
operationId must be camelCase (e.g. `getPublicIp`, `getCountryLocation`, `getAccountBalance`).
$.paths[*][get,post,put,patch,delete].operationId
error
operation-summary-required
Every operation must declare a `summary`.
$.paths[*][get,post,put,patch,delete]
warn
operation-summary-prefix-ipify
Operation summary must start with `ipify ` for consistent provider branding.
$.paths[*][get,post,put,patch,delete].summary
warn
operation-description-required
Every operation must declare a `description`.
$.paths[*][get,post,put,patch,delete]
error
operation-tags-required
Every operation must declare at least one tag.
$.paths[*][get,post,put,patch,delete]
warn
operation-microcks-extension-required
Every operation must declare an `x-microcks-operation` extension for mock-server compatibility.
$.paths[*][get,post,put,patch,delete]
warn
tags-root-defined
A root `tags` array must enumerate every tag with a description.
$
warn
tags-title-case
Tag names must be Title Case (e.g. `IP Address`, `Geolocation`, `Account`).
$.tags[*].name
warn
tag-description-required
Every declared tag must include a description.
$.tags[*]
warn
parameter-description-required
Every parameter must include a description.
#Parameter
warn
parameter-camelcase
Query/path parameter names must be camelCase (matches ipify's `apiKey`, `ipAddress`, `reverseIp`, `escapedUnicode`).
$.paths[*][*].parameters[?(@.in == 'query' || @.in == 'path')].name
error
parameter-schema-required
Every parameter must declare a `schema` with a `type`.
$.paths[*][*].parameters[*]
error
response-2xx-required
Every operation must declare at least one 2xx success response.
$.paths[*][get,post,put,patch,delete].responses
warn
response-description-required
Every response must include a description.
$.paths[*][*].responses[*]
info
response-error-codes-geolocation
Geolocation operations should document 400, 401, 403, 422, and 429 error responses.
$.paths[/country,/country\,city,/country\,city\,vpn][get].responses
warn
schema-camelcase-properties
Schema property names must be camelCase (matches the Geolocation API's `postalCode`, `geonameId`, `escapedUnicode`).
$.components.schemas[*].properties
warn
schema-description-required
Every top-level component schema must include a description.
$.components.schemas[*]
error
schema-type-required
Every top-level component schema must declare a `type`.
$.components.schemas[*]
info
security-scheme-apikey-query
The Geolocation API authenticates with an `apiKey` query parameter (no other schemes are supported).
$.components.securitySchemes[*]
info
security-public-api-no-auth
The free public IP API (`api.ipify.org`) must not declare a security scheme — it is intentionally unauthenticated.
$.paths[/].get
error
get-no-request-body
GET operations must not declare a request body — both ipify APIs are GET-only.
$.paths[*].get
error
ipify-get-only-api
ipify exposes only GET operations. Reject any POST/PUT/PATCH/DELETE.
$.paths[*]
info
deprecation-documented
Deprecated operations and schemas must be marked with `deprecated: true` (e.g. the VPN/proxy block).
$..deprecated
info
example-encouraged
Schema properties should include `example` values to support mock-server scenarios.
$.components.schemas[*].properties[*]

Spectral Ruleset

Raw ↑
rules:

  # ── INFO / METADATA ────────────────────────────────────────────────────
  info-title-required:
    description: Info object must define a title.
    severity: error
    given: $.info
    then:
      field: title
      function: truthy
  info-title-prefix-ipify:
    description: Spec title should start with `ipify` for consistent provider branding.
    severity: warn
    given: $.info.title
    then:
      function: pattern
      functionOptions:
        match: '^ipify'
  info-description-required:
    description: Info object must include a description of the API.
    severity: error
    given: $.info
    then:
      field: description
      function: truthy
  info-contact-required:
    description: Info object should include a contact for the provider.
    severity: warn
    given: $.info
    then:
      field: contact
      function: truthy
  info-license-required:
    description: Info object should declare a license (MIT for the free API, Commercial for Geolocation).
    severity: warn
    given: $.info
    then:
      field: license
      function: truthy

  # ── OPENAPI VERSION ────────────────────────────────────────────────────
  openapi-version-3:
    description: All ipify specs must declare OpenAPI 3.x.
    severity: error
    given: $.openapi
    then:
      function: pattern
      functionOptions:
        match: '^3\.'

  # ── SERVERS ────────────────────────────────────────────────────────────
  servers-required:
    description: A `servers` array must be defined at the document root.
    severity: error
    given: $
    then:
      field: servers
      function: truthy
  servers-must-be-https:
    description: ipify endpoints are HTTPS-only. No `http://` server URLs allowed.
    severity: error
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        notMatch: '^http://'
  servers-description-required:
    description: Each server entry must explain which surface it points to (IPv4, IPv6, dual-stack, account utility, etc.).
    severity: warn
    given: $.servers[*]
    then:
      field: description
      function: truthy

  # ── PATHS — NAMING CONVENTIONS ─────────────────────────────────────────
  paths-no-trailing-slash:
    description: Paths must not end with a trailing slash (except the root `/`).
    severity: error
    given: $.paths
    then:
      function: pattern
      functionOptions:
        match: '^(\/|[^\/]+(\/[^\/]+)*)$'
  paths-no-query-strings:
    description: Paths must not contain query strings — express them as parameters.
    severity: error
    given: $.paths
    then:
      function: pattern
      functionOptions:
        notMatch: '\?'
  paths-lowercase-segments:
    description: Path segments must be lowercase kebab-case (matches ipify's `/country`, `/country,city`, `/service/account-balance`).
    severity: warn
    given: $.paths
    then:
      function: pattern
      functionOptions:
        match: '^/[a-z0-9,_\-\/{}]*$'

  # ── OPERATIONS ─────────────────────────────────────────────────────────
  operation-operationId-required:
    description: Every operation must declare an `operationId`.
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: operationId
      function: truthy
  operation-operationId-camelcase:
    description: operationId must be camelCase (e.g. `getPublicIp`, `getCountryLocation`, `getAccountBalance`).
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].operationId
    then:
      function: pattern
      functionOptions:
        match: '^[a-z][a-zA-Z0-9]*$'
  operation-summary-required:
    description: Every operation must declare a `summary`.
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: summary
      function: truthy
  operation-summary-prefix-ipify:
    description: Operation summary must start with `ipify ` for consistent provider branding.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].summary
    then:
      function: pattern
      functionOptions:
        match: '^ipify '
  operation-description-required:
    description: Every operation must declare a `description`.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: description
      function: truthy
  operation-tags-required:
    description: Every operation must declare at least one tag.
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: tags
      function: truthy
  operation-microcks-extension-required:
    description: Every operation must declare an `x-microcks-operation` extension for mock-server compatibility.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: x-microcks-operation
      function: truthy

  # ── TAGS ───────────────────────────────────────────────────────────────
  tags-root-defined:
    description: A root `tags` array must enumerate every tag with a description.
    severity: warn
    given: $
    then:
      field: tags
      function: truthy
  tags-title-case:
    description: Tag names must be Title Case (e.g. `IP Address`, `Geolocation`, `Account`).
    severity: warn
    given: $.tags[*].name
    then:
      function: pattern
      functionOptions:
        match: '^([A-Z][A-Za-z]*)( [A-Z][A-Za-z]*)*$'
  tag-description-required:
    description: Every declared tag must include a description.
    severity: warn
    given: $.tags[*]
    then:
      field: description
      function: truthy

  # ── PARAMETERS ─────────────────────────────────────────────────────────
  parameter-description-required:
    description: Every parameter must include a description.
    severity: warn
    given: '#Parameter'
    then:
      field: description
      function: truthy
  parameter-camelcase:
    description: Query/path parameter names must be camelCase (matches ipify's `apiKey`, `ipAddress`, `reverseIp`, `escapedUnicode`).
    severity: warn
    given: $.paths[*][*].parameters[?(@.in == 'query' || @.in == 'path')].name
    then:
      function: pattern
      functionOptions:
        match: '^[a-z][a-zA-Z0-9]*$'
  parameter-schema-required:
    description: Every parameter must declare a `schema` with a `type`.
    severity: error
    given: $.paths[*][*].parameters[*]
    then:
      field: schema
      function: truthy

  # ── RESPONSES ──────────────────────────────────────────────────────────
  response-2xx-required:
    description: Every operation must declare at least one 2xx success response.
    severity: error
    given: $.paths[*][get,post,put,patch,delete].responses
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          patternProperties:
            '^2\d\d$': {}
          minProperties: 1
  response-description-required:
    description: Every response must include a description.
    severity: warn
    given: $.paths[*][*].responses[*]
    then:
      field: description
      function: truthy
  response-error-codes-geolocation:
    description: Geolocation operations should document 400, 401, 403, 422, and 429 error responses.
    severity: info
    given: $.paths[/country,/country\,city,/country\,city\,vpn][get].responses
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          required: ['400', '401', '403', '422', '429']

  # ── SCHEMAS — PROPERTY NAMING ──────────────────────────────────────────
  schema-camelcase-properties:
    description: Schema property names must be camelCase (matches the Geolocation API's `postalCode`, `geonameId`, `escapedUnicode`).
    severity: warn
    given: $.components.schemas[*].properties
    then:
      function: pattern
      functionOptions:
        match: '^[a-z][a-zA-Z0-9]*$'
  schema-description-required:
    description: Every top-level component schema must include a description.
    severity: warn
    given: $.components.schemas[*]
    then:
      field: description
      function: truthy
  schema-type-required:
    description: Every top-level component schema must declare a `type`.
    severity: error
    given: $.components.schemas[*]
    then:
      field: type
      function: truthy

  # ── SECURITY ───────────────────────────────────────────────────────────
  security-scheme-apikey-query:
    description: The Geolocation API authenticates with an `apiKey` query parameter (no other schemes are supported).
    severity: info
    given: $.components.securitySchemes[*]
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          properties:
            type: { const: apiKey }
            in: { const: query }
            name: { const: apiKey }
  security-public-api-no-auth:
    description: The free public IP API (`api.ipify.org`) must not declare a security scheme — it is intentionally unauthenticated.
    severity: info
    given: $.paths[/].get
    then:
      field: security
      function: falsy

  # ── HTTP METHOD CONVENTIONS ────────────────────────────────────────────
  get-no-request-body:
    description: GET operations must not declare a request body — both ipify APIs are GET-only.
    severity: error
    given: $.paths[*].get
    then:
      field: requestBody
      function: falsy
  ipify-get-only-api:
    description: ipify exposes only GET operations. Reject any POST/PUT/PATCH/DELETE.
    severity: error
    given: $.paths[*]
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          not:
            anyOf:
              - required: [post]
              - required: [put]
              - required: [patch]
              - required: [delete]

  # ── GENERAL QUALITY ────────────────────────────────────────────────────
  deprecation-documented:
    description: "Deprecated operations and schemas must be marked with `deprecated: true` (e.g. the VPN/proxy block)."
    severity: info
    given: $..deprecated
    then:
      function: truthy
  example-encouraged:
    description: Schema properties should include `example` values to support mock-server scenarios.
    severity: info
    given: $.components.schemas[*].properties[*]
    then:
      field: example
      function: truthy