Mindbody · API Governance Rules

Mindbody API Rules

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

44 Rules error 16 warn 23 info 5
View Rules File View on GitHub

Rule Categories

delete get global info oas operation parameter paths post request response schema security servers tags

Rules

error
info-title-mindbody-prefix
API title must begin with "Mindbody".
$.info.title
error
info-description-required
API description must be present and meaningful (>= 60 chars).
$.info
warn
info-description-length
Description should be at least 60 characters.
$.info.description
warn
info-contact-required
Mindbody contact block must be present.
$.info
warn
info-license-required
License must be declared.
$.info
error
oas-version-3
Use OpenAPI 3.0.x.
$.openapi
error
servers-defined
At least one server URL must be defined.
$.servers
error
servers-https
All server URLs must use HTTPS.
$.servers[*].url
warn
servers-mindbody-host
Server URLs must point at api.mindbodyonline.com or push-api.mindbodyonline.com.
$.servers[*].url
warn
paths-version-prefix
Public API paths must include the /public/v6 prefix; Webhooks paths use /api/v1.
$.paths.*~
error
paths-no-trailing-slash
Paths must not end with a trailing slash.
$.paths.*~
error
paths-no-query-strings
Paths must not contain query strings.
$.paths.*~
warn
paths-lowercase-segments
Path segments must be lowercase (Mindbody convention).
$.paths.*~
error
operation-operationid-required
Every operation must have an operationId.
$.paths.*[get,post,put,patch,delete]
warn
operation-operationid-camelcase
operationId should use camelCase.
$.paths.*[get,post,put,patch,delete].operationId
warn
operation-operationid-verb-prefix
operationId should start with a verb (get, list, add, update, delete, send, check, validate, search, upsert, return).
$.paths.*[get,post,put,patch,delete].operationId
error
operation-summary-required
Every operation must have a summary.
$.paths.*[get,post,put,patch,delete]
warn
operation-summary-mindbody-prefix
Operation summaries must begin with "Mindbody".
$.paths.*[get,post,put,patch,delete].summary
warn
operation-description-required
Every operation must have a description.
$.paths.*[get,post,put,patch,delete]
error
operation-tags-required
Every operation must have at least one tag.
$.paths.*[get,post,put,patch,delete]
warn
tags-defined-globally
Top-level tags array must be defined.
$
warn
tags-name-titlecase
Tag names should use Title Case (e.g. "Cross Site", "User Token").
$.tags[*].name
warn
tags-description-required
Every tag should have a description.
$.tags[*]
warn
parameter-description-required
Every parameter should have a description.
$.paths.*[get,post,put,patch,delete].parameters[*]
warn
parameter-siteid-header
SiteId must travel in the header, not the query.
$.paths.*[get,post,put,patch,delete].parameters[?(@.name=='SiteId' || @.name=='siteId')]
error
parameter-apikey-not-in-query
API-Key must never appear as a query parameter.
$.paths.*[get,post,put,patch,delete].parameters[?(@.name=='API-Key' || @.name=='ApiKey' || @.name=='api_key')]
info
parameter-pagination-naming
Pagination uses `request.limit` and `request.offset` (Mindbody convention).
$.paths.*[get].parameters[?(@.in=='query' && (@.name=='limit' || @.name=='offset'))]
error
request-body-json
Request bodies must offer application/json.
$.paths.*[post,put,patch].requestBody.content
error
response-success-required
Every operation must define a 2xx response.
$.paths.*[get,post,put,patch,delete].responses
warn
response-401-defined
Operations should document 401 Unauthorized.
$.paths.*[get,post,put,patch,delete].responses
info
response-404-defined
Operations should document 404 Not Found.
$.paths.*[get,post,put,patch,delete].responses
warn
response-description-required
Every response must have a description.
$.paths.*[get,post,put,patch,delete].responses.*
warn
schema-property-pascalcase
Mindbody JSON property names are PascalCase (e.g. ClientId, SiteId, Email).
$.components.schemas[*].properties.*~
info
schema-id-property-naming
Identifier properties should follow {Entity}Id PascalCase (ClientId, SiteId, StaffId).
$.components.schemas[*].properties[?(@property.match(/^[Ii]d$|^id_|^Id_/))]~
warn
schema-type-required
Schemas must declare `type`.
$.components.schemas.*
error
security-schemes-defined
At least one security scheme must be defined.
$.components.securitySchemes
error
security-apikey-header
Mindbody API key must travel in the `API-Key` request header.
$.components.securitySchemes[?(@.type=='apiKey')]
warn
security-oauth2-mindbody-urls
OAuth 2 flows must point at Mindbody Identity Service.
$.components.securitySchemes[?(@.type=='oauth2')].flows.authorizationCode
warn
global-security-defined
Global security requirement must be defined.
$
error
get-no-request-body
GET operations must not declare a request body.
$.paths.*.get
warn
delete-no-request-body
DELETE operations should not declare a request body.
$.paths.*.delete
info
post-has-body-or-explicit-query
POST operations should declare a request body when the operation mutates state.
$.paths.*.post
info
operation-microcks-extension
Every operation should carry an x-microcks-operation extension for mock-server compatibility.
$.paths.*[get,post,put,patch,delete]
warn
operation-deprecated-documented
Deprecated operations must explain the replacement in description.
$.paths.*[get,post,put,patch,delete][?(@.deprecated==true)]

Spectral Ruleset

Raw ↑
# Mindbody API Spectral Ruleset
#
# Generated by API Evangelist analysing the Mindbody Public API v6 and Webhooks API.
# Mindbody's specs use lowercased single-word path segments (e.g. /appointment/addons),
# PascalCase JSON property names (e.g. "ClientId", "SiteId"), camelCase query parameters
# (e.g. "request.endTime"), header-based API key auth (`API-Key`), and PascalCase tags
# tied to top-level business resources (Appointment, Class, Client, Sale, Site, Staff,
# Enrollment, Payroll, Cross Site, User Token). Webhooks live under /api/v1.
#
# Many of these rules are opinionated: they encode the *intended* shape of a
# next-generation, Spectral-compliant Mindbody contract — they are not a strict mirror
# of every quirk in the published v6 spec, which has accumulated legacy patterns.

extends:
  - spectral:oas

rules:

  # ─────────────────────────── INFO / METADATA ────────────────────────────
  info-title-mindbody-prefix:
    description: API title must begin with "Mindbody".
    message: '{{property}} must start with "Mindbody" (got "{{value}}").'
    given: "$.info.title"
    severity: error
    then:
      function: pattern
      functionOptions:
        match: "^Mindbody\\b"

  info-description-required:
    description: API description must be present and meaningful (>= 60 chars).
    given: "$.info"
    severity: error
    then:
      field: description
      function: truthy

  info-description-length:
    description: Description should be at least 60 characters.
    given: "$.info.description"
    severity: warn
    then:
      function: length
      functionOptions:
        min: 60

  info-contact-required:
    description: Mindbody contact block must be present.
    given: "$.info"
    severity: warn
    then:
      field: contact
      function: truthy

  info-license-required:
    description: License must be declared.
    given: "$.info"
    severity: warn
    then:
      field: license
      function: truthy

  # ─────────────────────────── OPENAPI VERSION ────────────────────────────
  oas-version-3:
    description: Use OpenAPI 3.0.x.
    given: "$.openapi"
    severity: error
    then:
      function: pattern
      functionOptions:
        match: "^3\\.0\\."

  # ─────────────────────────── SERVERS ────────────────────────────────────
  servers-defined:
    description: At least one server URL must be defined.
    given: "$.servers"
    severity: error
    then:
      function: length
      functionOptions:
        min: 1

  servers-https:
    description: All server URLs must use HTTPS.
    given: "$.servers[*].url"
    severity: error
    then:
      function: pattern
      functionOptions:
        match: "^https://"

  servers-mindbody-host:
    description: Server URLs must point at api.mindbodyonline.com or push-api.mindbodyonline.com.
    given: "$.servers[*].url"
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^https://(api|push-api)\\.mindbodyonline\\.com"

  # ─────────────────── PATHS — NAMING CONVENTIONS ─────────────────────────
  paths-version-prefix:
    description: 'Public API paths must include the /public/v6 prefix; Webhooks paths use /api/v1.'
    given: "$.paths.*~"
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^/(public/v6|api/v1)/"

  paths-no-trailing-slash:
    description: Paths must not end with a trailing slash.
    given: "$.paths.*~"
    severity: error
    then:
      function: pattern
      functionOptions:
        notMatch: ".+/$"

  paths-no-query-strings:
    description: Paths must not contain query strings.
    given: "$.paths.*~"
    severity: error
    then:
      function: pattern
      functionOptions:
        notMatch: "\\?"

  paths-lowercase-segments:
    description: Path segments must be lowercase (Mindbody convention).
    given: "$.paths.*~"
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^[a-z0-9/{}\\-_.]+$"

  # ─────────────────────────── OPERATIONS ─────────────────────────────────
  operation-operationid-required:
    description: Every operation must have an operationId.
    given: "$.paths.*[get,post,put,patch,delete]"
    severity: error
    then:
      field: operationId
      function: truthy

  operation-operationid-camelcase:
    description: operationId should use camelCase.
    given: "$.paths.*[get,post,put,patch,delete].operationId"
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^[a-z][a-zA-Z0-9]*$"

  operation-operationid-verb-prefix:
    description: 'operationId should start with a verb (get, list, add, update, delete, send, check, validate, search, upsert, return).'
    given: "$.paths.*[get,post,put,patch,delete].operationId"
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^(get|list|add|update|delete|create|remove|send|check|validate|search|upsert|return|cancel|issue)"

  operation-summary-required:
    description: Every operation must have a summary.
    given: "$.paths.*[get,post,put,patch,delete]"
    severity: error
    then:
      field: summary
      function: truthy

  operation-summary-mindbody-prefix:
    description: Operation summaries must begin with "Mindbody".
    given: "$.paths.*[get,post,put,patch,delete].summary"
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^Mindbody\\b"

  operation-description-required:
    description: Every operation must have a description.
    given: "$.paths.*[get,post,put,patch,delete]"
    severity: warn
    then:
      field: description
      function: truthy

  operation-tags-required:
    description: Every operation must have at least one tag.
    given: "$.paths.*[get,post,put,patch,delete]"
    severity: error
    then:
      field: tags
      function: schema
      functionOptions:
        schema:
          type: array
          minItems: 1

  # ─────────────────────────── TAGS ───────────────────────────────────────
  tags-defined-globally:
    description: Top-level tags array must be defined.
    given: "$"
    severity: warn
    then:
      field: tags
      function: truthy

  tags-name-titlecase:
    description: Tag names should use Title Case (e.g. "Cross Site", "User Token").
    given: "$.tags[*].name"
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^[A-Z][A-Za-z0-9]*( [A-Z][A-Za-z0-9]*)*$"

  tags-description-required:
    description: Every tag should have a description.
    given: "$.tags[*]"
    severity: warn
    then:
      field: description
      function: truthy

  # ─────────────────────────── PARAMETERS ─────────────────────────────────
  parameter-description-required:
    description: Every parameter should have a description.
    given: "$.paths.*[get,post,put,patch,delete].parameters[*]"
    severity: warn
    then:
      field: description
      function: truthy

  parameter-siteid-header:
    description: SiteId must travel in the header, not the query.
    message: SiteId must be a header parameter on Mindbody Public API operations.
    given: "$.paths.*[get,post,put,patch,delete].parameters[?(@.name=='SiteId' || @.name=='siteId')]"
    severity: warn
    then:
      field: in
      function: pattern
      functionOptions:
        match: "^header$"

  parameter-apikey-not-in-query:
    description: API-Key must never appear as a query parameter.
    given: "$.paths.*[get,post,put,patch,delete].parameters[?(@.name=='API-Key' || @.name=='ApiKey' || @.name=='api_key')]"
    severity: error
    then:
      field: in
      function: pattern
      functionOptions:
        notMatch: "^query$"

  parameter-pagination-naming:
    description: Pagination uses `request.limit` and `request.offset` (Mindbody convention).
    given: "$.paths.*[get].parameters[?(@.in=='query' && (@.name=='limit' || @.name=='offset'))]"
    severity: info
    message: 'Prefer `request.limit` / `request.offset` over bare `limit` / `offset` to match Mindbody Public API style.'
    then:
      function: falsy

  # ─────────────────────────── REQUEST BODIES ─────────────────────────────
  request-body-json:
    description: Request bodies must offer application/json.
    given: "$.paths.*[post,put,patch].requestBody.content"
    severity: error
    then:
      field: application/json
      function: truthy

  # ─────────────────────────── RESPONSES ──────────────────────────────────
  response-success-required:
    description: Every operation must define a 2xx response.
    given: "$.paths.*[get,post,put,patch,delete].responses"
    severity: error
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          patternProperties:
            "^2[0-9]{2}$": {}
          minProperties: 1
          additionalProperties: true

  response-401-defined:
    description: Operations should document 401 Unauthorized.
    given: "$.paths.*[get,post,put,patch,delete].responses"
    severity: warn
    then:
      field: "401"
      function: truthy

  response-404-defined:
    description: Operations should document 404 Not Found.
    given: "$.paths.*[get,post,put,patch,delete].responses"
    severity: info
    then:
      field: "404"
      function: truthy

  response-description-required:
    description: Every response must have a description.
    given: "$.paths.*[get,post,put,patch,delete].responses.*"
    severity: warn
    then:
      field: description
      function: truthy

  # ────────────────── SCHEMAS — PROPERTY NAMING ───────────────────────────
  schema-property-pascalcase:
    description: Mindbody JSON property names are PascalCase (e.g. ClientId, SiteId, Email).
    given: "$.components.schemas[*].properties.*~"
    severity: warn
    then:
      function: pattern
      functionOptions:
        match: "^[A-Z][A-Za-z0-9]*$"

  schema-id-property-naming:
    description: Identifier properties should follow {Entity}Id PascalCase (ClientId, SiteId, StaffId).
    given: "$.components.schemas[*].properties[?(@property.match(/^[Ii]d$|^id_|^Id_/))]~"
    severity: info
    message: Prefer fully-qualified PascalCase identifiers like ClientId, SiteId rather than bare `Id` or snake_case.
    then:
      function: falsy

  schema-type-required:
    description: Schemas must declare `type`.
    given: "$.components.schemas.*"
    severity: warn
    then:
      field: type
      function: truthy

  # ─────────────────────────── SECURITY ───────────────────────────────────
  security-schemes-defined:
    description: At least one security scheme must be defined.
    given: "$.components.securitySchemes"
    severity: error
    then:
      function: truthy

  security-apikey-header:
    description: Mindbody API key must travel in the `API-Key` request header.
    given: "$.components.securitySchemes[?(@.type=='apiKey')]"
    severity: error
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          properties:
            in: { const: header }
            name: { const: API-Key }
          required: [in, name]

  security-oauth2-mindbody-urls:
    description: OAuth 2 flows must point at Mindbody Identity Service.
    given: "$.components.securitySchemes[?(@.type=='oauth2')].flows.authorizationCode"
    severity: warn
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          properties:
            authorizationUrl:
              type: string
              pattern: "^https://signin\\.mindbodyonline\\.com/connect/authorize$"
            tokenUrl:
              type: string
              pattern: "^https://signin\\.mindbodyonline\\.com/connect/token$"

  global-security-defined:
    description: Global security requirement must be defined.
    given: "$"
    severity: warn
    then:
      field: security
      function: truthy

  # ─────────────────── HTTP METHOD CONVENTIONS ────────────────────────────
  get-no-request-body:
    description: GET operations must not declare a request body.
    given: "$.paths.*.get"
    severity: error
    then:
      field: requestBody
      function: falsy

  delete-no-request-body:
    description: DELETE operations should not declare a request body.
    given: "$.paths.*.delete"
    severity: warn
    then:
      field: requestBody
      function: falsy

  post-has-body-or-explicit-query:
    description: POST operations should declare a request body when the operation mutates state.
    given: "$.paths.*.post"
    severity: info
    then:
      field: requestBody
      function: truthy

  # ────────────────────────── MICROCKS ────────────────────────────────────
  operation-microcks-extension:
    description: Every operation should carry an x-microcks-operation extension for mock-server compatibility.
    given: "$.paths.*[get,post,put,patch,delete]"
    severity: info
    then:
      field: x-microcks-operation
      function: truthy

  # ────────────────────────── QUALITY ─────────────────────────────────────
  operation-deprecated-documented:
    description: Deprecated operations must explain the replacement in description.
    given: "$.paths.*[get,post,put,patch,delete][?(@.deprecated==true)]"
    severity: warn
    then:
      field: description
      function: pattern
      functionOptions:
        match: "(?i)(deprecated|replaced|migrate)"