Memesio · API Governance Rules

Memesio API Rules

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

18 Rules error 5 warn 7
View Rules File View on GitHub

Rule Categories

memesio

Rules

warn
memesio-info-title-required
API info.title must include "Memesio".
$.info.title
error
memesio-info-version-required
API info.version must be present and follow semver-like format.
$.info.version
error
memesio-paths-must-start-with-api
All paths must be served under /api (Memesio routes prefix all surfaces with /api).
$.paths.*~
warn
memesio-paths-kebab-case
Path segments must use kebab-case (lowercase with hyphens), not snake_case or camelCase.
$.paths.*~
hint
memesio-versioned-paths-under-v1
Long-lived versioned routes use /api/v1/* (e.g. /api/v1/agents, /api/v1/memes, /api/v1/templates).
$.paths[?(@property.match(/\/api\/v\d+\//) && [email protected]('/api/v1/'))]~
warn
memesio-operation-summary-title-case
Operation summaries must be in Title Case.
$.paths[*][get,post,put,patch,delete].summary
error
memesio-operation-summary-required
Every operation must have a non-empty summary.
$.paths[*][get,post,put,patch,delete]
error
memesio-operation-must-have-tags
Every operation must declare at least one tag.
$.paths[*][get,post,put,patch,delete]
hint
memesio-operation-tag-vocabulary
Tags should come from the Memesio domain vocabulary.
$.paths[*][get,post,put,patch,delete].tags[*]
warn
memesio-response-codes-required
Operations should declare at least one 2xx response.
$.paths[*][get,post,put,patch,delete].responses
hint
memesio-ai-routes-grouped
AI features live under /api/ai/* or are explicitly tagged "ai".
$.paths[?(@property.match(/(caption|generate|face-swap|background-remove|moderate)/) && [email protected](/\/api\/ai\//) && [email protected](/\/free\//))]~
hint
memesio-ai-quota-get-pairing
Mutating AI endpoints under /api/ai/captions and /api/ai/memes should expose a sibling GET for quota inspection.
$.paths[?(@property.match(/\/api\/ai\/(captions|memes)\/generate$/))]
error
memesio-free-routes-tagged-public-free
Anonymous routes under /api/free/* must carry the "public-free" tag.
$.paths[?(@property.startsWith('/api/free/'))][get,post].tags
warn
memesio-agent-routes-tagged
Agent identity and key management routes under /api/v1/agents/* must be tagged "agent-infra" or "developer-api".
$.paths[?(@property.startsWith('/api/v1/agents'))][get,post,patch,delete].tags
hint
memesio-list-pagination-shape
List endpoints that paginate must expose `page` and `pageSize` query parameters.
$.paths[*].get.parameters
warn
memesio-pagesize-bounded
pageSize parameters should declare a maximum.
$.paths[*][get].parameters[?(@.name=='pageSize')].schema
warn
memesio-schema-names-pascal-case
Component schemas must be PascalCase.
$.components.schemas.*~
hint
memesio-meme-summary-required-fields
MemeSummary should expose stable identifiers and a hosted URL.
$.components.schemas.MemeSummary.properties

Spectral Ruleset

Raw ↑
extends:
  - "spectral:oas"
documentationUrl: https://memesio.com/developers/api
description: |
  Spectral ruleset enforcing the conventions observed in Memesio's OpenAPI
  contract (https://memesio.com/api/openapi). Covers naming, tagging,
  authentication, AI quota signaling, public/free routes, agent identity, and
  caption/template content discipline.

functions: []

rules:
  # === Provider info ============================================================
  memesio-info-title-required:
    description: API info.title must include "Memesio".
    message: '{{property}} should mention "Memesio" — "{{value}}" does not.'
    severity: warn
    given: $.info.title
    then:
      function: pattern
      functionOptions:
        match: "(?i)Memesio"

  memesio-info-version-required:
    description: API info.version must be present and follow semver-like format.
    severity: error
    given: $.info.version
    then:
      function: pattern
      functionOptions:
        match: "^\\d+\\.\\d+\\.\\d+(-[A-Za-z0-9.]+)?$"

  # === Path conventions =========================================================
  memesio-paths-must-start-with-api:
    description: All paths must be served under /api (Memesio routes prefix all surfaces with /api).
    message: '{{path}} should start with /api/'
    severity: error
    given: $.paths.*~
    then:
      function: pattern
      functionOptions:
        match: "^/api/"

  memesio-paths-kebab-case:
    description: Path segments must use kebab-case (lowercase with hyphens), not snake_case or camelCase.
    message: 'Path segment in {{path}} should be kebab-case.'
    severity: warn
    given: $.paths.*~
    then:
      function: pattern
      functionOptions:
        match: "^(/(api|v1|v\\d+)|/[a-z0-9-]+|/\\{[a-zA-Z]+\\})+$"

  memesio-versioned-paths-under-v1:
    description: Long-lived versioned routes use /api/v1/* (e.g. /api/v1/agents, /api/v1/memes, /api/v1/templates).
    message: '{{path}} appears to be a versioned route but does not use /api/v1/.'
    severity: hint
    given: $.paths[?(@property.match(/\/api\/v\d+\//) && [email protected]('/api/v1/'))]~
    then:
      function: falsy

  # === Operation conventions ====================================================
  memesio-operation-summary-title-case:
    description: Operation summaries must be in Title Case.
    message: 'Summary "{{value}}" should be Title Case.'
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].summary
    then:
      function: pattern
      functionOptions:
        match: "^[A-Z]"

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

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

  memesio-operation-tag-vocabulary:
    description: Tags should come from the Memesio domain vocabulary.
    message: 'Tag "{{value}}" is outside the known Memesio tag vocabulary.'
    severity: hint
    given: $.paths[*][get,post,put,patch,delete].tags[*]
    then:
      function: enumeration
      functionOptions:
        values:
          - agent-infra
          - ai
          - ai-captions
          - ai-jobs
          - ai-providers
          - analytics
          - audio
          - auth
          - backend
          - background-remove
          - billing
          - channels
          - collaboration
          - compliance
          - data-eng
          - data-ops
          - data-science
          - developer-api
          - distribution
          - experimentation
          - face-swap
          - finops
          - growth
          - history
          - lifecycle
          - marketing-ops
          - media
          - memes
          - moderation
          - motion
          - notifications
          - observability
          - performance
          - personalization
          - platform
          - product-marketing
          - public-free
          - safety
          - templates
          - trend-alerts
          - uploads
          - video

  memesio-response-codes-required:
    description: Operations should declare at least one 2xx response.
    severity: warn
    given: $.paths[*][get,post,put,patch,delete].responses
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          patternProperties:
            "^2\\d{2}$":
              type: object

  # === AI / quota routes ========================================================
  memesio-ai-routes-grouped:
    description: AI features live under /api/ai/* or are explicitly tagged "ai".
    message: '{{path}} looks AI-related but lives outside /api/ai/.'
    severity: hint
    given: $.paths[?(@property.match(/(caption|generate|face-swap|background-remove|moderate)/) && [email protected](/\/api\/ai\//) && [email protected](/\/free\//))]~
    then:
      function: falsy

  memesio-ai-quota-get-pairing:
    description: Mutating AI endpoints under /api/ai/captions and /api/ai/memes should expose a sibling GET for quota inspection.
    severity: hint
    given: $.paths[?(@property.match(/\/api\/ai\/(captions|memes)\/generate$/))]
    then:
      field: get
      function: truthy

  # === Public/free routes =======================================================
  memesio-free-routes-tagged-public-free:
    description: Anonymous routes under /api/free/* must carry the "public-free" tag.
    severity: error
    given: $.paths[?(@property.startsWith('/api/free/'))][get,post].tags
    then:
      function: schema
      functionOptions:
        schema:
          type: array
          contains:
            const: "public-free"

  # === Agent identity routes ====================================================
  memesio-agent-routes-tagged:
    description: Agent identity and key management routes under /api/v1/agents/* must be tagged "agent-infra" or "developer-api".
    severity: warn
    given: $.paths[?(@property.startsWith('/api/v1/agents'))][get,post,patch,delete].tags
    then:
      function: schema
      functionOptions:
        schema:
          type: array
          contains:
            anyOf:
              - const: "agent-infra"
              - const: "developer-api"

  # === Pagination ===============================================================
  memesio-list-pagination-shape:
    description: List endpoints that paginate must expose `page` and `pageSize` query parameters.
    severity: hint
    given: $.paths[*].get.parameters
    then:
      function: schema
      functionOptions:
        schema:
          type: array

  memesio-pagesize-bounded:
    description: pageSize parameters should declare a maximum.
    severity: warn
    given: $.paths[*][get].parameters[?(@.name=='pageSize')].schema
    then:
      field: maximum
      function: truthy

  # === Schemas ==================================================================
  memesio-schema-names-pascal-case:
    description: Component schemas must be PascalCase.
    severity: warn
    given: $.components.schemas.*~
    then:
      function: pattern
      functionOptions:
        match: "^[A-Z][A-Za-z0-9]+$"

  memesio-meme-summary-required-fields:
    description: MemeSummary should expose stable identifiers and a hosted URL.
    severity: hint
    given: $.components.schemas.MemeSummary.properties
    then:
      function: schema
      functionOptions:
        schema:
          type: object
          required: [slug]