Workday Tracking System · API Governance Rules

Workday Tracking System API Rules

Spectral linting rules defining API design standards and conventions for Workday Tracking System.

68 Rules error 18 warn 39 info 11
View Rules File View on GitHub

Rule Categories

delete deprecation external get info no openapi operation parameter patch paths post put request response schema security servers tags

Rules

warn
info-title-format
API title should follow the "Workday ..." naming pattern
$.info.title
error
info-description-required
API info must have a description
$.info
warn
info-description-min-length
API description should be at least 50 characters
$.info.description
error
info-version-required
API info must specify a version
$.info
warn
info-version-format
API version should follow the "v{N}" pattern (e.g. v1)
$.info.version
warn
info-contact-required
API info should include contact information
$.info
warn
info-contact-name
Contact should include a name
$.info.contact
warn
info-contact-url
Contact should include a developer URL
$.info.contact
error
openapi-version
APIs must use OpenAPI 3.0.x
$.openapi
error
servers-defined
At least one server must be defined
$
error
servers-https-only
All server URLs must use HTTPS
$.servers[*].url
warn
servers-tenant-pattern
Server URLs should follow the Workday tenant pattern (e.g. https://{tenant}.workday.com/api/{domain}/v{N})
$.servers[*].url
warn
servers-description-required
Servers should have descriptions
$.servers[*]
warn
paths-camel-case
Path segments should use camelCase (Workday convention)
$.paths[*]~
error
paths-no-trailing-slash
Paths must not have trailing slashes
$.paths[*]~
error
paths-no-query-string
Paths must not contain query strings
$.paths[*]~
warn
paths-no-version-prefix
API version belongs in the server URL, not in the path
$.paths[*]~
info
paths-worker-scoped-when-applicable
Worker-specific resources should be scoped under /workers/{workerId}
$.paths[*]~
error
operation-summary-required
All operations must have a summary
$.paths[*][get,post,put,patch,delete]
warn
operation-summary-title-case
Operation summaries should use Title Case (e.g., "List Time Blocks")
$.paths[*][get,post,put,patch,delete].summary
warn
operation-description-required
All operations should have a description
$.paths[*][get,post,put,patch,delete]
error
operation-id-required
All operations must have an operationId
$.paths[*][get,post,put,patch,delete]
warn
operation-id-camel-case
operationId should use camelCase
$.paths[*][get,post,put,patch,delete].operationId
warn
operation-id-verb-prefix
operationId should start with a recognized verb (list, get, create, update, delete, request, submit, assign, import, override)
$.paths[*][get,post,put,patch,delete].operationId
error
operation-tags-required
All operations must have at least one tag
$.paths[*][get,post,put,patch,delete]
info
operation-single-tag
Operations should have exactly one tag for clean grouping
$.paths[*][get,post,put,patch,delete].tags
warn
tags-defined-globally
Global tags array should be defined at the root
$
warn
tags-title-case
Tags should use Title Case (e.g. "Time Blocks", "Leave of Absence")
$.tags[*].name
warn
tags-description-required
All global tags should have descriptions
$.tags[*]
error
parameter-description-required
All parameters must have descriptions
$.paths[*][get,post,put,patch,delete].parameters[*]
warn
parameter-camel-case
Parameter names should use camelCase (Workday convention)
$.paths[*][get,post,put,patch,delete].parameters[*].name
error
parameter-schema-required
All parameters must define a schema
$.paths[*][get,post,put,patch,delete].parameters[*]
warn
parameter-pagination-offset
Use "offset" (not "skip", "from", or "page") for pagination offset
$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')].name
warn
parameter-pagination-limit
Use "limit" (not "size", "count", or "perPage") for pagination size
$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')].name
info
parameter-date-range-naming
Date range filters should use startDate/endDate (Workday convention)
$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')].name
warn
parameter-worker-id-naming
Worker identifier path parameter should be named "workerId"
$.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'path' && @.name =~ /worker/i)].name
warn
request-body-description
Request bodies should have descriptions
$.paths[*][post,put,patch].requestBody
error
request-body-json-content
Request bodies must support application/json
$.paths[*][post,put,patch].requestBody.content
warn
request-body-schema-ref
Request body schemas should reference a named component (not be inlined)
$.paths[*][post,put,patch].requestBody.content.application/json.schema
error
response-success-required
All operations must define at least one response
$.paths[*][get,post,put,patch,delete].responses
error
response-description-required
All responses must have a description
$.paths[*][get,post,put,patch,delete].responses[*]
warn
response-401-required
All operations should define a 401 Unauthorized response
$.paths[*][get,post,put,patch,delete].responses
warn
response-404-required-for-resource-paths
GET/PUT/DELETE on resource paths should define a 404 Not Found response
$.paths[?(@property.match(/\\{[a-zA-Z]+\\}$/))][get,put,delete].responses
warn
response-400-for-mutations
POST/PUT/PATCH operations should define a 400 Bad Request response
$.paths[*][post,put,patch].responses
warn
response-success-json-content
2xx responses with content should use application/json
$.paths[*][get,post,put,patch,delete].responses[?(@property.match(/^2[0-9]{2}$/))].content
info
response-error-schema
Error responses should reference an error schema with message and error fields
$.components.schemas.ErrorResponse.properties
info
response-delete-204
DELETE operations should return 204 No Content
$.paths[*].delete.responses
warn
schema-property-camel-case
Schema property names should use camelCase (Workday convention)
$.components.schemas[*].properties[*]~
warn
schema-type-defined
Top-level schemas must define a type
$.components.schemas[*]
warn
schema-description-required
Top-level schemas should have a description
$.components.schemas[*]
info
schema-id-property-naming
Identifier properties should be named "id" or end with "Id" (e.g., workerId, timeBlockId)
$.components.schemas[*].properties[?(@property.match(/^[a-zA-Z]*[Ii]dentifier$/))]~
info
schema-timestamp-naming
Timestamp properties should follow the createdAt/modifiedAt convention
$.components.schemas[*].properties[?(@property.match(/^(created|modified|updated)[_-]?(date|time|on)$/i))]~
info
schema-required-array
Required fields should be declared in a "required" array, not on individual properties
$.components.schemas[*].properties[*]
warn
security-global-defined
Global security requirements should be defined
$
error
security-schemes-defined
Security schemes must be defined under components
$
warn
security-bearer-scheme
Workday APIs should expose a BearerAuth scheme using Bearer/JWT
$.components.securitySchemes.BearerAuth
warn
security-bearer-format
Bearer security schemes should declare bearerFormat (e.g. JWT)
$.components.securitySchemes[?(@.scheme == 'bearer')]
warn
security-scheme-description
Security schemes should have a description
$.components.securitySchemes[*]
error
get-no-request-body
GET operations must not have a request body
$.paths[*].get
warn
delete-no-request-body
DELETE operations should not have a request body
$.paths[*].delete
warn
post-has-request-body
POST operations should have a request body
$.paths[*].post
warn
put-has-request-body
PUT operations should have a request body
$.paths[*].put
warn
patch-has-request-body
PATCH operations should have a request body
$.paths[*].patch
error
no-empty-descriptions
Descriptions must not be empty strings
$..description
info
schema-properties-have-descriptions
Schema properties should have descriptions
$.components.schemas[*].properties[*]
info
schema-properties-have-examples
Schema properties should include examples
$.components.schemas[*].properties[?(@.type && @.type != 'object' && @.type != 'array')]
warn
deprecation-documented
Deprecated operations should explain the deprecation in the description
$.paths[*][get,post,put,patch,delete][?(@.deprecated == true)]
info
external-docs-encouraged
APIs should link to external documentation
$

Spectral Ruleset

Raw ↑
rules:

  # =====================================================================
  # INFO / METADATA
  # =====================================================================
  info-title-format:
    description: API title should follow the "Workday ..." naming pattern
    severity: warn
    given: $.info.title
    then:
      function: pattern
      functionOptions:
        match: "^Workday .+ API$"

  info-description-required:
    description: API info must have a description
    severity: error
    given: $.info
    then:
      field: description
      function: truthy

  info-description-min-length:
    description: API description should be at least 50 characters
    severity: warn
    given: $.info.description
    then:
      function: length
      functionOptions:
        min: 50

  info-version-required:
    description: API info must specify a version
    severity: error
    given: $.info
    then:
      field: version
      function: truthy

  info-version-format:
    description: API version should follow the "v{N}" pattern (e.g. v1)
    severity: warn
    given: $.info.version
    then:
      function: pattern
      functionOptions:
        match: "^v[0-9]+$"

  info-contact-required:
    description: API info should include contact information
    severity: warn
    given: $.info
    then:
      field: contact
      function: truthy

  info-contact-name:
    description: Contact should include a name
    severity: warn
    given: $.info.contact
    then:
      field: name
      function: truthy

  info-contact-url:
    description: Contact should include a developer URL
    severity: warn
    given: $.info.contact
    then:
      field: url
      function: truthy

  # =====================================================================
  # OPENAPI VERSION
  # =====================================================================
  openapi-version:
    description: APIs must use OpenAPI 3.0.x
    severity: error
    given: $.openapi
    then:
      function: pattern
      functionOptions:
        match: "^3\\.0\\."

  # =====================================================================
  # SERVERS
  # =====================================================================
  servers-defined:
    description: At least one server must be defined
    severity: error
    given: $
    then:
      field: servers
      function: truthy

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

  servers-tenant-pattern:
    description: Server URLs should follow the Workday tenant pattern (e.g. https://{tenant}.workday.com/api/{domain}/v{N})
    severity: warn
    given: $.servers[*].url
    then:
      function: pattern
      functionOptions:
        match: "^https://\\{tenant\\}\\.workday\\.com/api/[a-z][a-z-]*/v[0-9]+$"

  servers-description-required:
    description: Servers should have descriptions
    severity: warn
    given: $.servers[*]
    then:
      field: description
      function: truthy

  # =====================================================================
  # PATHS — NAMING CONVENTIONS
  # =====================================================================
  paths-camel-case:
    description: Path segments should use camelCase (Workday convention)
    severity: warn
    given: $.paths[*]~
    then:
      function: pattern
      functionOptions:
        match: "^(/([a-z][a-zA-Z0-9]*|\\{[a-zA-Z][a-zA-Z0-9]*\\}))+$"

  paths-no-trailing-slash:
    description: Paths must not have trailing slashes
    severity: error
    given: $.paths[*]~
    then:
      function: pattern
      functionOptions:
        notMatch: "/$"

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

  paths-no-version-prefix:
    description: API version belongs in the server URL, not in the path
    severity: warn
    given: $.paths[*]~
    then:
      function: pattern
      functionOptions:
        notMatch: "^/v[0-9]+(/|$)"

  paths-worker-scoped-when-applicable:
    description: Worker-specific resources should be scoped under /workers/{workerId}
    severity: info
    given: $.paths[*]~
    then:
      function: pattern
      functionOptions:
        notMatch: "^/(timeBlocks|timeOff|timeRequests|timesheets|timeClockEvents|workSchedule|leaveOfAbsence|balances|accruals)(/|$)"

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

  operation-summary-title-case:
    description: Operation summaries should use Title Case (e.g., "List Time Blocks")
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].summary
    then:
      function: pattern
      functionOptions:
        match: "^[A-Z][A-Za-z0-9]*(\\s[A-Z0-9][A-Za-z0-9-]*)*$"

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

  operation-id-required:
    description: All operations must have an operationId
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: operationId
      function: truthy

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

  operation-id-verb-prefix:
    description: operationId should start with a recognized verb (list, get, create, update, delete, request, submit, assign, import, override)
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].operationId
    then:
      function: pattern
      functionOptions:
        match: "^(list|get|create|update|delete|request|submit|assign|import|override|cancel|approve|deny|return)[A-Z][a-zA-Z0-9]+$"

  operation-tags-required:
    description: All operations must have at least one tag
    severity: error
    given: $.paths[*][get,post,put,patch,delete]
    then:
      field: tags
      function: truthy

  operation-single-tag:
    description: Operations should have exactly one tag for clean grouping
    severity: info
    given: $.paths[*][get,post,put,patch,delete].tags
    then:
      function: length
      functionOptions:
        max: 1

  # =====================================================================
  # TAGS
  # =====================================================================
  tags-defined-globally:
    description: Global tags array should be defined at the root
    severity: warn
    given: $
    then:
      field: tags
      function: truthy

  tags-title-case:
    description: Tags should use Title Case (e.g. "Time Blocks", "Leave of Absence")
    severity: warn
    given: $.tags[*].name
    then:
      function: pattern
      functionOptions:
        match: "^[A-Z][a-zA-Z]*(\\s(of|to|for|and|the|in|on)|\\s[A-Z][a-zA-Z]*)*$"

  tags-description-required:
    description: All global tags should have descriptions
    severity: warn
    given: $.tags[*]
    then:
      field: description
      function: truthy

  # =====================================================================
  # PARAMETERS
  # =====================================================================
  parameter-description-required:
    description: All parameters must have descriptions
    severity: error
    given: $.paths[*][get,post,put,patch,delete].parameters[*]
    then:
      field: description
      function: truthy

  parameter-camel-case:
    description: Parameter names should use camelCase (Workday convention)
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].parameters[*].name
    then:
      function: pattern
      functionOptions:
        match: "^[a-z][a-zA-Z0-9]*$"

  parameter-schema-required:
    description: All parameters must define a schema
    severity: error
    given: $.paths[*][get,post,put,patch,delete].parameters[*]
    then:
      field: schema
      function: truthy

  parameter-pagination-offset:
    description: Use "offset" (not "skip", "from", or "page") for pagination offset
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')].name
    then:
      function: pattern
      functionOptions:
        notMatch: "^(skip|from|page|pageNumber|pageNum)$"

  parameter-pagination-limit:
    description: Use "limit" (not "size", "count", or "perPage") for pagination size
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')].name
    then:
      function: pattern
      functionOptions:
        notMatch: "^(size|count|perPage|pageSize)$"

  parameter-date-range-naming:
    description: Date range filters should use startDate/endDate (Workday convention)
    severity: info
    given: $.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'query')].name
    then:
      function: pattern
      functionOptions:
        notMatch: "^(fromDate|toDate|dateFrom|dateTo|begin|end)$"

  parameter-worker-id-naming:
    description: Worker identifier path parameter should be named "workerId"
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].parameters[?(@.in == 'path' && @.name =~ /worker/i)].name
    then:
      function: enumeration
      functionOptions:
        values:
          - workerId

  # =====================================================================
  # REQUEST BODIES
  # =====================================================================
  request-body-description:
    description: Request bodies should have descriptions
    severity: warn
    given: $.paths[*][post,put,patch].requestBody
    then:
      field: description
      function: truthy

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

  request-body-schema-ref:
    description: Request body schemas should reference a named component (not be inlined)
    severity: warn
    given: $.paths[*][post,put,patch].requestBody.content.application/json.schema
    then:
      field: $ref
      function: truthy

  # =====================================================================
  # RESPONSES
  # =====================================================================
  response-success-required:
    description: All operations must define at least one response
    severity: error
    given: $.paths[*][get,post,put,patch,delete].responses
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          minProperties: 1

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

  response-401-required:
    description: All operations should define a 401 Unauthorized response
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].responses
    then:
      field: '401'
      function: truthy

  response-404-required-for-resource-paths:
    description: GET/PUT/DELETE on resource paths should define a 404 Not Found response
    severity: warn
    given: $.paths[?(@property.match(/\\{[a-zA-Z]+\\}$/))][get,put,delete].responses
    then:
      field: '404'
      function: truthy

  response-400-for-mutations:
    description: POST/PUT/PATCH operations should define a 400 Bad Request response
    severity: warn
    given: $.paths[*][post,put,patch].responses
    then:
      field: '400'
      function: truthy

  response-success-json-content:
    description: 2xx responses with content should use application/json
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].responses[?(@property.match(/^2[0-9]{2}$/))].content
    then:
      field: application/json
      function: truthy

  response-error-schema:
    description: Error responses should reference an error schema with message and error fields
    severity: info
    given: $.components.schemas.ErrorResponse.properties
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          required:
            - error
            - message

  response-delete-204:
    description: DELETE operations should return 204 No Content
    severity: info
    given: $.paths[*].delete.responses
    then:
      field: '204'
      function: truthy

  # =====================================================================
  # SCHEMAS — PROPERTY NAMING
  # =====================================================================
  schema-property-camel-case:
    description: Schema property names should use camelCase (Workday convention)
    severity: warn
    given: $.components.schemas[*].properties[*]~
    then:
      function: pattern
      functionOptions:
        match: "^[a-z][a-zA-Z0-9]*$"

  schema-type-defined:
    description: Top-level schemas must define a type
    severity: warn
    given: $.components.schemas[*]
    then:
      field: type
      function: truthy

  schema-description-required:
    description: Top-level schemas should have a description
    severity: warn
    given: $.components.schemas[*]
    then:
      field: description
      function: truthy

  schema-id-property-naming:
    description: Identifier properties should be named "id" or end with "Id" (e.g., workerId, timeBlockId)
    severity: info
    given: $.components.schemas[*].properties[?(@property.match(/^[a-zA-Z]*[Ii]dentifier$/))]~
    then:
      function: pattern
      functionOptions:
        notMatch: ".*"

  schema-timestamp-naming:
    description: Timestamp properties should follow the createdAt/modifiedAt convention
    severity: info
    given: $.components.schemas[*].properties[?(@property.match(/^(created|modified|updated)[_-]?(date|time|on)$/i))]~
    then:
      function: pattern
      functionOptions:
        notMatch: ".*"

  schema-required-array:
    description: Required fields should be declared in a "required" array, not on individual properties
    severity: info
    given: $.components.schemas[*].properties[*]
    then:
      field: required
      function: falsy

  # =====================================================================
  # SECURITY
  # =====================================================================
  security-global-defined:
    description: Global security requirements should be defined
    severity: warn
    given: $
    then:
      field: security
      function: truthy

  security-schemes-defined:
    description: Security schemes must be defined under components
    severity: error
    given: $
    then:
      field: components.securitySchemes
      function: truthy

  security-bearer-scheme:
    description: Workday APIs should expose a BearerAuth scheme using Bearer/JWT
    severity: warn
    given: $.components.securitySchemes.BearerAuth
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          required:
            - type
            - scheme
          properties:
            type:
              const: http
            scheme:
              const: bearer

  security-bearer-format:
    description: Bearer security schemes should declare bearerFormat (e.g. JWT)
    severity: warn
    given: $.components.securitySchemes[?(@.scheme == 'bearer')]
    then:
      field: bearerFormat
      function: truthy

  security-scheme-description:
    description: Security schemes should have a description
    severity: warn
    given: $.components.securitySchemes[*]
    then:
      field: description
      function: truthy

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

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

  post-has-request-body:
    description: POST operations should have a request body
    severity: warn
    given: $.paths[*].post
    then:
      field: requestBody
      function: truthy

  put-has-request-body:
    description: PUT operations should have a request body
    severity: warn
    given: $.paths[*].put
    then:
      field: requestBody
      function: truthy

  patch-has-request-body:
    description: PATCH operations should have a request body
    severity: warn
    given: $.paths[*].patch
    then:
      field: requestBody
      function: truthy

  # =====================================================================
  # GENERAL QUALITY
  # =====================================================================
  no-empty-descriptions:
    description: Descriptions must not be empty strings
    severity: error
    given: $..description
    then:
      function: pattern
      functionOptions:
        match: ".+"

  schema-properties-have-descriptions:
    description: Schema properties should have descriptions
    severity: info
    given: $.components.schemas[*].properties[*]
    then:
      field: description
      function: truthy

  schema-properties-have-examples:
    description: Schema properties should include examples
    severity: info
    given: $.components.schemas[*].properties[?(@.type && @.type != 'object' && @.type != 'array')]
    then:
      field: example
      function: truthy

  deprecation-documented:
    description: Deprecated operations should explain the deprecation in the description
    severity: warn
    given: $.paths[*][get,post,put,patch,delete][?(@.deprecated == true)]
    then:
      field: description
      function: truthy

  external-docs-encouraged:
    description: APIs should link to external documentation
    severity: info
    given: $
    then:
      field: externalDocs
      function: truthy