Unkey · API Governance Rules

Unkey API Rules

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

10 Rules error 3 warn 7
View Rules File View on GitHub

Rule Categories

unkey

Rules

warn
unkey-post-only-management
Management endpoints should use POST method with dot-notation operationId
$.paths[*]
error
unkey-operation-id-dot-notation
Operation IDs must use dot-notation (e.g. keys.createKey, ratelimit.limit)
$.paths[*][*].operationId
error
unkey-path-versioning
All API paths must start with /v2/
$.paths[*]~
warn
unkey-bearer-auth
All operations (except liveness) must require rootKey authentication
$.paths[?(!@property.match(/liveness/))][*]
warn
unkey-response-envelope
Success responses must use the meta+data envelope structure
$.paths[*][*].responses['200'].content['application/json'].schema
warn
unkey-title-case-summary
Operation summaries must use Title Case
$.paths[*][*].summary
warn
unkey-valid-tags
Operations must use defined API resource tags
$.paths[*][*].tags[*]
error
unkey-json-request-body
Request bodies must use application/json media type
$.paths[*][*].requestBody.content
warn
unkey-error-response-401
Operations requiring auth must define 401 responses
$.paths[?(!@property.match(/liveness/))][*]
warn
unkey-error-response-403
Operations with permissions must define 403 responses
$.paths[*][*]

Spectral Ruleset

Raw ↑
extends: [[spectral:oas, all]]

rules:

  # Unkey uses POST for all management endpoints with dot-notation operation IDs
  unkey-post-only-management:
    description: Management endpoints should use POST method with dot-notation operationId
    message: "{{description}}: {{path}}"
    severity: warn
    given: "$.paths[*]"
    then:
      - field: post
        function: truthy

  # Operation IDs must use dot-notation (resource.action pattern)
  unkey-operation-id-dot-notation:
    description: Operation IDs must use dot-notation (e.g. keys.createKey, ratelimit.limit)
    message: "OperationId '{{value}}' should use dot-notation like 'resource.action'"
    severity: error
    given: "$.paths[*][*].operationId"
    then:
      function: pattern
      functionOptions:
        match: "^[a-z][a-zA-Z0-9]*\\.[a-z][a-zA-Z0-9]+$"

  # All paths must start with /v2/
  unkey-path-versioning:
    description: All API paths must start with /v2/
    message: "Path '{{path}}' must be versioned with /v2/ prefix"
    severity: error
    given: "$.paths[*]~"
    then:
      function: pattern
      functionOptions:
        match: "^/v2/"

  # All operations must use rootKey security
  unkey-bearer-auth:
    description: All operations (except liveness) must require rootKey authentication
    message: "{{description}}: {{path}}"
    severity: warn
    given: "$.paths[?(!@property.match(/liveness/))][*]"
    then:
      - field: security
        function: truthy

  # Responses must wrap data in envelope with meta.requestId
  unkey-response-envelope:
    description: Success responses must use the meta+data envelope structure
    message: "200 response must include meta and data properties"
    severity: warn
    given: "$.paths[*][*].responses['200'].content['application/json'].schema"
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          required: [meta]
          properties:
            meta:
              type: object

  # Operation summaries must be in Title Case
  unkey-title-case-summary:
    description: Operation summaries must use Title Case
    message: "Summary '{{value}}' should use Title Case"
    severity: warn
    given: "$.paths[*][*].summary"
    then:
      function: pattern
      functionOptions:
        match: "^[A-Z]"

  # Tags must be one of the defined resource groups
  unkey-valid-tags:
    description: Operations must use defined API resource tags
    message: "Tag '{{value}}' must be one of: analytics, apis, deploy, identities, keys, liveness, permissions, ratelimit"
    severity: warn
    given: "$.paths[*][*].tags[*]"
    then:
      function: enumeration
      functionOptions:
        values:
          - analytics
          - apis
          - deploy
          - identities
          - keys
          - liveness
          - permissions
          - ratelimit

  # Request bodies must use application/json
  unkey-json-request-body:
    description: Request bodies must use application/json media type
    message: "{{description}}: {{path}}"
    severity: error
    given: "$.paths[*][*].requestBody.content"
    then:
      function: truthy
      field: "application/json"

  # Error responses must use consistent error response schemas
  unkey-error-response-401:
    description: Operations requiring auth must define 401 responses
    message: "Authenticated operation missing 401 response"
    severity: warn
    given: "$.paths[?(!@property.match(/liveness/))][*]"
    then:
      field: "responses.401"
      function: truthy

  # 403 responses for permission-gated operations
  unkey-error-response-403:
    description: Operations with permissions must define 403 responses
    message: "Permission-gated operation missing 403 response"
    severity: warn
    given: "$.paths[*][*]"
    then:
      field: "responses.403"
      function: truthy

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/unkey-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 email required.

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