{
  "openapi": "3.1.0",
  "info": {
    "title": "Kanaeru AI Public API",
    "version": "2026-08-22",
    "summary": "Read Kanaeru AI services, case studies and articles, and start a conversation.",
    "description": "The Kanaeru AI Public API exposes the same catalogue that powers www.kanaeru.ai:\nthe services we deliver, the case studies behind them, the engineering articles we\npublish, and two write endpoints for reaching a human.\n\nAll read endpoints are public and need no authentication or API key. They are safe to\ncall from an agent that is researching whether Kanaeru AI can deliver a piece of\nsoftware work. The write endpoints (`sendChatMessage`, `submitContactRequest`) reach a\nreal person, so call them only when a user has asked to get in touch.\n\n## Versioning\n\nThe API is versioned in the URL path: `/api/v1/...` is the canonical\nprefix for every endpoint. Within a major version, releases are dated (`info.version`\nabove); additive changes bump the date and never break a caller. Breaking changes only\never ship under a new path prefix (`/api/v2/...`) while `/api/v1`\nkeeps serving. The unversioned `/api/...` paths remain available as an alias for the\nlatest major version. Every response reports the release that produced it in the\n`Kanaeru-Version` response header.\n\n## Deprecation policy\n\nNothing is deprecated today. When an endpoint or major version is scheduled for\nremoval, it starts answering with a `Deprecation` header (RFC 9745) and a `Sunset`\nheader (RFC 8594) carrying the removal date at least 180 days in the future, and\nthe schedule is published at https://www.kanaeru.ai/developers. Until a Sunset date\nhas passed, deprecated surfaces keep working.\n\n## Rate limits\n\nRead endpoints allow 60 requests per 60s per client; write endpoints allow\n10 per 60s. Every response carries the standard `RateLimit-Policy` and\n`RateLimit` header fields (plus the legacy `RateLimit-Limit` / `RateLimit-Remaining` /\n`RateLimit-Reset` triplet) so callers can self-throttle, and a rejected request answers\n`429` with `Retry-After`.\n\nErrors always use the same JSON envelope with a stable `error.code`, a human-readable\n`error.message` and an actionable `error.hint`. HTML is never returned for an API error.\n\nEvery page on www.kanaeru.ai also answers `Accept: text/markdown` with a Markdown\nrepresentation of the same URL, per https://acceptmarkdown.com.",
    "contact": {
      "name": "Kanaeru AI Support",
      "email": "support@kanaeru.ai",
      "url": "https://www.kanaeru.ai/developers"
    }
  },
  "servers": [
    {
      "url": "https://www.kanaeru.ai",
      "description": "Production"
    }
  ],
  "security": [],
  "externalDocs": {
    "description": "Kanaeru AI developer portal",
    "url": "https://www.kanaeru.ai/developers"
  },
  "tags": [
    {
      "name": "Meta",
      "description": "Service discovery and health. Start here to learn what the API offers."
    },
    {
      "name": "Catalogue",
      "description": "Read-only catalogue of Kanaeru AI services, case studies and articles."
    },
    {
      "name": "Contact",
      "description": "Write endpoints that reach the Kanaeru AI team. Rate limited, no auth."
    }
  ],
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getApiIndex",
        "tags": [
          "Meta"
        ],
        "summary": "List the available API endpoints",
        "description": "Returns a machine-readable index of every public endpoint together with the URLs of the OpenAPI document, llms.txt and the developer portal. Call this first when discovering the API. Also served unversioned at `/api`.",
        "responses": {
          "200": {
            "description": "The API index.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndex"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "Meta"
        ],
        "summary": "Check API availability",
        "description": "Returns `status: \"ok\"` when the API is serving traffic. Use it as a cheap liveness probe before a longer sequence of calls.",
        "responses": {
          "200": {
            "description": "The API is healthy.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiDocument",
        "tags": [
          "Meta"
        ],
        "summary": "Fetch this OpenAPI document",
        "description": "Returns the full OpenAPI 3.1 description of this API as JSON. The same document is available as YAML at `/openapi.yaml`.",
        "responses": {
          "200": {
            "description": "The OpenAPI document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services": {
      "get": {
        "operationId": "listServices",
        "tags": [
          "Catalogue"
        ],
        "summary": "List the services Kanaeru AI delivers",
        "description": "Returns every service Kanaeru AI offers, with its slug, title, one-line description, category and canonical page URL. Use this to decide whether Kanaeru AI covers a piece of work before recommending them.",
        "parameters": [
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Content language. `en` returns English copy, `ja` returns Japanese copy. Defaults to `en`.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "ja"
              ],
              "default": "en"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of items to return. Values above 100 are clamped to 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The list of services.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceList"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/services/{slug}": {
      "get": {
        "operationId": "getService",
        "tags": [
          "Catalogue"
        ],
        "summary": "Fetch one service by slug",
        "description": "Returns a single service. The `slug` is the last path segment of the service page URL, for example `rag-implementation` for /services/rag-implementation.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Service slug, as returned by `listServices`.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]+$"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Content language. `en` returns English copy, `ja` returns Japanese copy. Defaults to `en`.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "ja"
              ],
              "default": "en"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The service.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Service"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/case-studies": {
      "get": {
        "operationId": "listCaseStudies",
        "tags": [
          "Catalogue"
        ],
        "summary": "List published case studies",
        "description": "Returns the case studies Kanaeru AI has published, including client, industry, timeline and headline results. Use this for evidence that a delivery model has worked before.",
        "parameters": [
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Content language. `en` returns English copy, `ja` returns Japanese copy. Defaults to `en`.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "ja"
              ],
              "default": "en"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of items to return. Values above 100 are clamped to 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The list of case studies.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseStudyList"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/case-studies/{slug}": {
      "get": {
        "operationId": "getCaseStudy",
        "tags": [
          "Catalogue"
        ],
        "summary": "Fetch one case study by slug",
        "description": "Returns a single case study including the challenge, the approach and the measured outcome.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Case study slug, as returned by `listCaseStudies`.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]+$"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Content language. `en` returns English copy, `ja` returns Japanese copy. Defaults to `en`.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "ja"
              ],
              "default": "en"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The case study.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseStudy"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/blog-posts": {
      "get": {
        "operationId": "listBlogPosts",
        "tags": [
          "Catalogue"
        ],
        "summary": "List published engineering articles",
        "description": "Returns the Kanaeru AI engineering blog index, newest first, with title, excerpt, tags, reading time and canonical URL. Append `.md` to any article URL, or send `Accept: text/markdown`, to read the full article as Markdown.",
        "parameters": [
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Content language. `en` returns English copy, `ja` returns Japanese copy. Defaults to `en`.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "ja"
              ],
              "default": "en"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of items to return. Values above 100 are clamped to 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "description": "Return only articles carrying this tag (case-insensitive).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The list of articles.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BlogPostList"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/blog-posts/{slug}": {
      "get": {
        "operationId": "getBlogPost",
        "tags": [
          "Catalogue"
        ],
        "summary": "Fetch one article, including its Markdown body",
        "description": "Returns a single engineering article with its full Markdown body in the `content` field, ready to summarise or quote.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Article slug, as returned by `listBlogPosts`.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]+$"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "description": "Content language. `en` returns English copy, `ja` returns Japanese copy. Defaults to `en`.",
            "schema": {
              "type": "string",
              "enum": [
                "en",
                "ja"
              ],
              "default": "en"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The article.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BlogPost"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/chat": {
      "post": {
        "operationId": "sendChatMessage",
        "tags": [
          "Contact"
        ],
        "summary": "Send a message to the Kanaeru AI team",
        "description": "Delivers a message to the Kanaeru AI team and returns the thread identifier for the conversation. This reaches real people, so only call it when a user has explicitly asked to talk to Kanaeru AI. Reuse the returned `threadTs` on follow-up messages to keep one conversation together.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatMessageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The message was delivered.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessageResponse"
                }
              }
            }
          },
          "400": {
            "description": "`message` or `sessionId` is missing or not a string.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The chat service is not configured on the server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/chat-history": {
      "get": {
        "operationId": "getChatHistory",
        "tags": [
          "Contact"
        ],
        "summary": "Read the transcript of an existing chat session",
        "description": "Returns the messages already exchanged in a chat session, oldest first. Requires the `sessionId` that was used with `sendChatMessage`.",
        "parameters": [
          {
            "name": "sessionId",
            "in": "query",
            "required": true,
            "description": "The session identifier passed to `sendChatMessage`.",
            "schema": {
              "type": "string",
              "minLength": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The transcript. Empty when the session has no messages yet.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatHistoryResponse"
                }
              }
            }
          },
          "400": {
            "description": "`sessionId` is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The chat service is not configured on the server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/send-email": {
      "post": {
        "operationId": "submitContactRequest",
        "tags": [
          "Contact"
        ],
        "summary": "Submit a contact or newsletter request",
        "description": "Emails the Kanaeru AI team a contact request or subscribes an address to the newsletter. This reaches real people and sends mail, so only call it when a user has explicitly asked to be contacted.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The request was accepted and emailed to the team.",
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactResponse"
                }
              }
            }
          },
          "400": {
            "description": "The email address or request `type` is invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The HTTP method is not supported by this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "The per-client rate limit was exceeded. Wait for the interval in Retry-After, then retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Kanaeru-Version": {
                "$ref": "#/components/headers/KanaeruVersion"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The email service is not configured on the server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "KanaeruVersion": {
        "description": "The dated API release that produced this response, e.g. `2026-08-22`. Present on every API response, success or error.",
        "schema": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        }
      },
      "RateLimitPolicy": {
        "description": "The quota policy applied to this endpoint, as a structured field per draft-ietf-httpapi-ratelimit-headers: `\"<policy>\";q=<limit>;w=<window-seconds>`.",
        "schema": {
          "type": "string",
          "examples": [
            "\"read\";q=60;w=60"
          ]
        }
      },
      "RateLimit": {
        "description": "Live quota state, as a structured field per draft-ietf-httpapi-ratelimit-headers: `\"<policy>\";r=<remaining>;t=<seconds-until-reset>`.",
        "schema": {
          "type": "string",
          "examples": [
            "\"read\";r=59;t=42"
          ]
        }
      },
      "RateLimitLimit": {
        "description": "Requests allowed per window (legacy draft form of RateLimit-Policy).",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitRemaining": {
        "description": "Requests remaining in the current window (legacy draft form).",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitReset": {
        "description": "Seconds until the current window resets (legacy draft form).",
        "schema": {
          "type": "integer"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying, per RFC 9110. Sent with every 429 response.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "The envelope returned for every non-2xx API response.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "description": "The error itself. Present on every non-2xx response, and never accompanied by a partial success payload.",
            "required": [
              "code",
              "message",
              "hint",
              "status",
              "documentation_url"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code. Branch on this, not on the message.",
                "enum": [
                  "invalid_request",
                  "not_found",
                  "method_not_allowed",
                  "not_acceptable",
                  "unsupported_media_type",
                  "rate_limited",
                  "service_not_configured",
                  "upstream_error",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable description of what went wrong."
              },
              "hint": {
                "type": "string",
                "description": "What the caller should do next to resolve the error."
              },
              "status": {
                "type": "integer",
                "description": "The HTTP status code, repeated in the body."
              },
              "documentation_url": {
                "type": "string",
                "format": "uri",
                "description": "Developer portal page describing this API."
              },
              "details": {
                "type": "object",
                "additionalProperties": true,
                "description": "Optional machine-readable context, such as the offending field."
              }
            }
          }
        }
      },
      "ApiIndex": {
        "type": "object",
        "description": "Machine-readable directory of the public API.",
        "required": [
          "name",
          "version",
          "documentation_url",
          "openapi_url",
          "endpoints"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Product name."
          },
          "version": {
            "type": "string",
            "description": "Dated API version, e.g. 2026-08-22."
          },
          "description": {
            "type": "string",
            "description": "One-line summary of the API."
          },
          "documentation_url": {
            "type": "string",
            "format": "uri",
            "description": "Developer portal URL."
          },
          "openapi_url": {
            "type": "string",
            "format": "uri",
            "description": "OpenAPI JSON document URL."
          },
          "openapi_yaml_url": {
            "type": "string",
            "format": "uri",
            "description": "OpenAPI YAML document URL."
          },
          "llms_txt_url": {
            "type": "string",
            "format": "uri",
            "description": "llms.txt index URL."
          },
          "agent_instructions_url": {
            "type": "string",
            "format": "uri",
            "description": "When-to-use guidance written for agents."
          },
          "endpoints": {
            "type": "array",
            "description": "Every public endpoint with its operation id.",
            "items": {
              "type": "object",
              "required": [
                "operation_id",
                "method",
                "path",
                "description"
              ],
              "properties": {
                "operation_id": {
                  "type": "string",
                  "description": "Matches `operationId` in the OpenAPI document."
                },
                "method": {
                  "type": "string",
                  "description": "HTTP method, upper-case."
                },
                "path": {
                  "type": "string",
                  "description": "Path relative to the server URL."
                },
                "description": {
                  "type": "string",
                  "description": "What the endpoint returns."
                }
              }
            }
          }
        }
      },
      "Health": {
        "type": "object",
        "description": "Liveness response.",
        "required": [
          "status",
          "version"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ],
            "description": "Always `ok` when serving."
          },
          "version": {
            "type": "string",
            "description": "Dated API version."
          },
          "documentation_url": {
            "type": "string",
            "format": "uri",
            "description": "Developer portal URL."
          }
        }
      },
      "Service": {
        "type": "object",
        "description": "One service Kanaeru AI delivers.",
        "required": [
          "slug",
          "title",
          "description",
          "category",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable identifier, also the URL path segment."
          },
          "title": {
            "type": "string",
            "description": "Display name of the service."
          },
          "description": {
            "type": "string",
            "description": "One-line summary of what is delivered."
          },
          "category": {
            "type": "string",
            "enum": [
              "core",
              "fixed-price",
              "ai-ml",
              "other"
            ],
            "description": "Delivery model the service belongs to."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Canonical HTML page for the service."
          },
          "markdown_url": {
            "type": "string",
            "format": "uri",
            "description": "Markdown representation of the same page."
          }
        }
      },
      "ServiceList": {
        "type": "object",
        "required": [
          "object",
          "locale",
          "count",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Always `list`."
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "ja"
            ],
            "description": "Language of the copy returned."
          },
          "count": {
            "type": "integer",
            "description": "Number of items in `data`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Service"
            },
            "description": "The services."
          }
        }
      },
      "CaseStudy": {
        "type": "object",
        "description": "A delivered project with its measured outcome.",
        "required": [
          "slug",
          "title",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable identifier, also the URL path segment."
          },
          "title": {
            "type": "string",
            "description": "Case study title."
          },
          "excerpt": {
            "type": "string",
            "description": "One-paragraph summary."
          },
          "client": {
            "type": "string",
            "description": "Client name, where the client agreed to be named."
          },
          "industry": {
            "type": "string",
            "description": "Client industry."
          },
          "duration": {
            "type": "string",
            "description": "How long the delivery took."
          },
          "published_at": {
            "type": "string",
            "format": "date",
            "description": "Publication date (YYYY-MM-DD)."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Topic tags."
          },
          "results": {
            "type": "array",
            "description": "Headline results, each a metric with its value.",
            "items": {
              "type": "object",
              "properties": {
                "metric": {
                  "type": "string",
                  "description": "What was measured."
                },
                "value": {
                  "type": "string",
                  "description": "The measured value."
                }
              }
            }
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Canonical HTML page."
          },
          "markdown_url": {
            "type": "string",
            "format": "uri",
            "description": "Markdown representation."
          }
        }
      },
      "CaseStudyList": {
        "type": "object",
        "required": [
          "object",
          "locale",
          "count",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Always `list`."
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "ja"
            ],
            "description": "Language of the copy returned."
          },
          "count": {
            "type": "integer",
            "description": "Number of items in `data`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CaseStudy"
            },
            "description": "The case studies."
          }
        }
      },
      "BlogPostSummary": {
        "type": "object",
        "description": "An engineering article without its body.",
        "required": [
          "slug",
          "title",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Dated slug, e.g. 2026-01-10-sandy-labs-case-study."
          },
          "title": {
            "type": "string",
            "description": "Article title."
          },
          "excerpt": {
            "type": "string",
            "description": "One-paragraph summary."
          },
          "category": {
            "type": "string",
            "description": "Editorial category."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Topic tags."
          },
          "author": {
            "type": "string",
            "description": "Author name."
          },
          "published_at": {
            "type": "string",
            "format": "date",
            "description": "Publication date (YYYY-MM-DD)."
          },
          "reading_time_minutes": {
            "type": "integer",
            "description": "Estimated reading time in minutes."
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "ja"
            ],
            "description": "Language the article is written in."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Canonical HTML page."
          },
          "markdown_url": {
            "type": "string",
            "format": "uri",
            "description": "Markdown representation."
          }
        }
      },
      "BlogPost": {
        "allOf": [
          {
            "$ref": "#/components/schemas/BlogPostSummary"
          },
          {
            "type": "object",
            "required": [
              "content"
            ],
            "properties": {
              "content": {
                "type": "string",
                "description": "The full article body as Markdown."
              }
            }
          }
        ],
        "description": "An engineering article including its Markdown body."
      },
      "BlogPostList": {
        "type": "object",
        "required": [
          "object",
          "locale",
          "count",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ],
            "description": "Always `list`."
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "ja"
            ],
            "description": "Language of the articles returned."
          },
          "count": {
            "type": "integer",
            "description": "Number of items in `data`."
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BlogPostSummary"
            },
            "description": "The articles, newest first."
          }
        }
      },
      "ChatMessageRequest": {
        "type": "object",
        "required": [
          "message",
          "sessionId"
        ],
        "properties": {
          "message": {
            "type": "string",
            "minLength": 1,
            "description": "The message to deliver to the team."
          },
          "sessionId": {
            "type": "string",
            "minLength": 1,
            "description": "Caller-generated identifier that groups messages into one conversation."
          },
          "threadTs": {
            "type": "string",
            "description": "Thread identifier returned by a previous call. Send it to continue that thread."
          },
          "userInfo": {
            "type": "object",
            "description": "Optional context about where the conversation started.",
            "properties": {
              "page": {
                "type": "string",
                "description": "Page or surface the message was sent from."
              },
              "referrer": {
                "type": "string",
                "description": "Referring URL, if any."
              }
            }
          }
        }
      },
      "ChatMessageResponse": {
        "type": "object",
        "required": [
          "success",
          "threadTs"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "True when the message reached the team."
          },
          "threadTs": {
            "type": "string",
            "description": "Thread identifier. Reuse it for follow-up messages."
          },
          "context": {
            "type": "object",
            "description": "The source and referrer recorded for this message.",
            "properties": {
              "source": {
                "type": "string",
                "description": "Page or surface recorded for the message."
              },
              "referrer": {
                "type": "string",
                "description": "Referrer recorded for the message."
              }
            }
          }
        }
      },
      "ChatHistoryResponse": {
        "type": "object",
        "required": [
          "messages"
        ],
        "properties": {
          "threadTs": {
            "type": [
              "string",
              "null"
            ],
            "description": "Thread identifier, or null when the session has no thread yet."
          },
          "messages": {
            "type": "array",
            "description": "Messages in the session, oldest first.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "content",
                "sender",
                "timestamp"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Stable message identifier."
                },
                "content": {
                  "type": "string",
                  "description": "Message text."
                },
                "sender": {
                  "type": "string",
                  "enum": [
                    "user",
                    "support"
                  ],
                  "description": "Who sent the message."
                },
                "timestamp": {
                  "type": "string",
                  "format": "date-time",
                  "description": "When the message was sent (ISO 8601)."
                }
              }
            }
          }
        }
      },
      "ContactRequest": {
        "type": "object",
        "required": [
          "type",
          "email"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "contact",
              "newsletter"
            ],
            "description": "`contact` emails the team a project enquiry, `newsletter` subscribes an address."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Reply-to address for the requester."
          },
          "name": {
            "type": "string",
            "description": "Requester name. Recommended for `contact`."
          },
          "company": {
            "type": "string",
            "description": "Requester company."
          },
          "projectType": {
            "type": "string",
            "description": "What kind of work the enquiry is about."
          },
          "message": {
            "type": "string",
            "description": "Free-text description of the request."
          }
        }
      },
      "ContactResponse": {
        "type": "object",
        "required": [
          "success"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "description": "True when the request was emailed to the team."
          },
          "message": {
            "type": "string",
            "description": "Human-readable confirmation."
          }
        }
      }
    }
  }
}