{
  "openapi": "3.1.0",
  "info": {
    "title": "Kai public API (hirekai.ai)",
    "version": "1.0.0",
    "summary": "Read-only content, discovery files and the Content MCP for Kai, the AI executive assistant by Morgen.",
    "description": "Everything on hirekai.ai an agent can call without an account. No endpoint here needs authentication, and none of them reads or changes anyone's data.\n\nTo act on a signed-in user's own calendar, email, tasks and meetings, use Kai's product MCP server at https://api.morgen.so/mcp-kai (Streamable HTTP, OAuth 2.1; the user approves once on Kai's consent screen, and there's no API key). Setup per AI client: https://hirekai.ai/kai-mcp.\n\nErrors on /api/* are JSON (`{ \"error\": \"not_found\", \"message\": …, \"hint\": …, \"docs\": … }`). A missing page requested with `Accept: text/markdown` returns a markdown 404. Human docs: https://hirekai.ai/developers.",
    "contact": {
      "name": "Kai team",
      "url": "https://hirekai.ai/contact"
    }
  },
  "externalDocs": {
    "description": "Developer docs",
    "url": "https://hirekai.ai/developers"
  },
  "servers": [
    {
      "url": "https://hirekai.ai",
      "description": "Production"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "discovery",
      "description": "Files that tell an agent what Kai is and where everything lives."
    },
    {
      "name": "content",
      "description": "Markdown versions of the site's pages."
    },
    {
      "name": "mcp",
      "description": "The Content MCP: Kai's product knowledge as MCP tools and resources."
    },
    {
      "name": "account",
      "description": "Handoffs into the Kai app."
    },
    {
      "name": "status",
      "description": "Service health."
    }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "status"
        ],
        "summary": "Check the site is up",
        "description": "Returns `{\"status\":\"ok\"}` while hirekai.ai serves requests. Cached for 60 seconds.",
        "responses": {
          "200": {
            "description": "The site is up.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "operationId": "getLlmsTxt",
        "tags": [
          "discovery"
        ],
        "summary": "Get llms.txt",
        "description": "Short summary of Kai, when an agent should use it, and links to every machine-readable resource (llmstxt.org format).",
        "responses": {
          "200": {
            "description": "The llms.txt file.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms-full.txt": {
      "get": {
        "operationId": "getLlmsFullTxt",
        "tags": [
          "discovery"
        ],
        "summary": "Get the full knowledge base as text",
        "description": "Kai's whole product knowledge base in one plain-text file: features, integrations, pricing, terminology.",
        "responses": {
          "200": {
            "description": "The full knowledge base.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/AGENTS.md": {
      "get": {
        "operationId": "getAgentsGuide",
        "tags": [
          "discovery"
        ],
        "summary": "Get the agent integration guide",
        "description": "How coding and chat agents discover Kai, read its content and call both MCP servers, with example calls.",
        "responses": {
          "200": {
            "description": "The integration guide.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/sitemap.md": {
      "get": {
        "operationId": "getMarkdownSitemap",
        "tags": [
          "discovery"
        ],
        "summary": "List every public page (markdown)",
        "description": "Every public page with its title, grouped by section; pages with a markdown mirror are marked.",
        "responses": {
          "200": {
            "description": "The markdown sitemap.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "operationId": "getXmlSitemap",
        "tags": [
          "discovery"
        ],
        "summary": "List every public page (XML)",
        "description": "The sitemaps.org XML sitemap search engines read.",
        "responses": {
          "200": {
            "description": "The XML sitemap.",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiSpec",
        "tags": [
          "discovery"
        ],
        "summary": "Get this OpenAPI document",
        "description": "The OpenAPI 3.1 description of hirekai.ai's public endpoints (this file).",
        "responses": {
          "200": {
            "description": "This document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/api-catalog": {
      "get": {
        "operationId": "getApiCatalog",
        "tags": [
          "discovery"
        ],
        "summary": "Get the API catalog (RFC 9727)",
        "description": "Linkset (RFC 9264) of the public APIs, each with its docs and status links.",
        "responses": {
          "200": {
            "description": "The API catalog.",
            "content": {
              "application/linkset+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/agent-skills/index.json": {
      "get": {
        "operationId": "getAgentSkillsIndex",
        "tags": [
          "discovery"
        ],
        "summary": "List agent skills",
        "description": "Agent Skills Discovery index (v0.2.0): the actions an agent can take for a user, each with a markdown doc and its sha256.",
        "responses": {
          "200": {
            "description": "The skills index.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/mcp/server-card.json": {
      "get": {
        "operationId": "getMcpServerCard",
        "tags": [
          "discovery",
          "mcp"
        ],
        "summary": "Get the MCP server card",
        "description": "SEP-1649 server card for the Content MCP at /mcp, plus a pointer to Kai's product MCP server.",
        "responses": {
          "200": {
            "description": "The server card.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/md/home": {
      "get": {
        "operationId": "getHomepageMarkdown",
        "tags": [
          "content"
        ],
        "summary": "Get the homepage as markdown",
        "description": "The homepage's content as markdown. Requesting `/` with `Accept: text/markdown` returns the same document.",
        "responses": {
          "200": {
            "description": "The homepage in markdown.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/{feature}.md": {
      "get": {
        "operationId": "getFeaturePageMarkdown",
        "tags": [
          "content"
        ],
        "summary": "Get a feature page as markdown",
        "description": "One of Kai's feature pages (email, meetings, daily planning, action items, Ask Kai) as markdown with YAML frontmatter.",
        "parameters": [
          {
            "name": "feature",
            "in": "path",
            "required": true,
            "description": "Feature page slug.",
            "schema": {
              "type": "string",
              "enum": [
                "action-items",
                "ask-kai",
                "daily-planning",
                "email",
                "meetings"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The feature page in markdown.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/compare/{competitor}.md": {
      "get": {
        "operationId": "getComparisonMarkdown",
        "tags": [
          "content"
        ],
        "summary": "Get a Kai-vs-competitor comparison as markdown",
        "description": "A comparison page (Kai vs another tool) as markdown with YAML frontmatter.",
        "parameters": [
          {
            "name": "competitor",
            "in": "path",
            "required": true,
            "description": "Competitor slug.",
            "schema": {
              "type": "string",
              "enum": [
                "fyxer-ai",
                "granola-ai",
                "otter-ai"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The comparison in markdown.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No comparison with that slug."
          }
        }
      }
    },
    "/blog/{slug}.md": {
      "get": {
        "operationId": "getBlogPostMarkdown",
        "tags": [
          "content"
        ],
        "summary": "Get a blog post as markdown",
        "description": "A blog post as markdown with YAML frontmatter. Slugs are listed in /sitemap.md, or call the `list_blog_posts` tool on /mcp. `/blog/{slug}` with `Accept: text/markdown` returns the same document.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "description": "Post slug, as in https://hirekai.ai/blog/{slug}.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The post in markdown.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No post with that slug."
          }
        }
      }
    },
    "/blog/how-we-grow/{issue}.md": {
      "get": {
        "operationId": "getHowWeGrowIssueMarkdown",
        "tags": [
          "content"
        ],
        "summary": "Get a How We Grow issue as markdown",
        "description": "An issue of the How We Grow series as markdown with YAML frontmatter.",
        "parameters": [
          {
            "name": "issue",
            "in": "path",
            "required": true,
            "description": "Issue slug, as in https://hirekai.ai/blog/how-we-grow/{issue}.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The issue in markdown.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No issue with that slug."
          }
        }
      }
    },
    "/api/md/skills/{name}": {
      "get": {
        "operationId": "getAgentSkillDoc",
        "tags": [
          "discovery"
        ],
        "summary": "Get one agent skill's doc",
        "description": "The markdown doc for one skill in the agent skills index: when to use it and how to call it.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Skill name.",
            "schema": {
              "type": "string",
              "enum": [
                "contact",
                "start-using-kai"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The skill doc.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No skill with that name."
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "callContentMcp",
        "tags": [
          "mcp"
        ],
        "summary": "Call the Content MCP (JSON-RPC 2.0)",
        "description": "Kai's Content MCP over Streamable HTTP, stateless: every POST is one JSON-RPC 2.0 message and there's no session id. Start with `initialize`, then `tools/list`, `tools/call`, `resources/list` or `resources/read`. Tools: search_kai, get_feature, get_product_info, list_blog_posts, get_blog_post, compare_kai_with, get_free_tool. Read-only, unauthenticated, no user data. Limit: 60 requests a minute per network.",
        "parameters": [
          {
            "name": "Accept",
            "in": "header",
            "required": true,
            "description": "Must list both application/json and text/event-stream (MCP Streamable HTTP).",
            "schema": {
              "type": "string",
              "example": "application/json, text/event-stream"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              },
              "examples": {
                "initialize": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "initialize",
                    "params": {
                      "protocolVersion": "2025-06-18",
                      "capabilities": {},
                      "clientInfo": {
                        "name": "my-agent",
                        "version": "1.0.0"
                      }
                    }
                  }
                },
                "search": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/call",
                    "params": {
                      "name": "search_kai",
                      "arguments": {
                        "query": "botless meeting notes",
                        "limit": 5
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The JSON-RPC response, as one server-sent event (`event: message`, `data: {…}`).",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "202": {
            "description": "A notification (no `id`) was accepted. No body."
          },
          "406": {
            "description": "The Accept header doesn't list both application/json and text/event-stream."
          },
          "429": {
            "description": "More than 60 requests a minute from this network.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimpleError"
                }
              }
            }
          }
        }
      }
    },
    "/get-started": {
      "get": {
        "operationId": "startSignup",
        "tags": [
          "account"
        ],
        "summary": "Send a person to Kai's sign-up",
        "description": "Redirects to the Kai app (https://web.hirekai.ai), where sign-up is open and free to start. Link a person here; it isn't an API, and the account is created in the app.",
        "responses": {
          "302": {
            "description": "Redirect to the app's sign-up.",
            "headers": {
              "Location": {
                "description": "The app URL.",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          }
        }
      }
    },
    "/api/contact": {
      "post": {
        "operationId": "submitContactInquiry",
        "tags": [
          "account"
        ],
        "summary": "Submit the contact form",
        "description": "The endpoint behind https://hirekai.ai/contact. It only accepts requests from that form (the browser's Origin must be https://hirekai.ai); anything else gets 403. An agent should send the person to https://hirekai.ai/contact rather than post here. Limit: 3 submissions per 15 minutes per network.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactInquiry"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Delivered. The team answers by email.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "sent"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body; `fields` names each field that failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationError"
                }
              }
            }
          },
          "403": {
            "description": "The request didn't come from the hirekai.ai contact form.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimpleError"
                }
              }
            }
          },
          "429": {
            "description": "Too many submissions from this network.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SimpleError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Health": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Error body for any /api/* path that doesn't exist.",
        "required": [
          "error",
          "status",
          "message",
          "hint",
          "docs"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Machine-readable code.",
            "examples": [
              "not_found"
            ]
          },
          "status": {
            "type": "integer",
            "description": "HTTP status, repeated.",
            "examples": [
              404
            ]
          },
          "message": {
            "type": "string",
            "description": "What went wrong."
          },
          "hint": {
            "type": "string",
            "description": "What to try next."
          },
          "docs": {
            "type": "string",
            "format": "uri",
            "description": "Where the docs are."
          }
        }
      },
      "SimpleError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "What went wrong, in words."
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "required": [
          "error",
          "fields"
        ],
        "properties": {
          "error": {
            "type": "string",
            "const": "Validation failed"
          },
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Field name to the reason it failed."
          }
        }
      },
      "ContactInquiry": {
        "type": "object",
        "required": [
          "name",
          "email",
          "category",
          "message"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 100
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254
          },
          "category": {
            "type": "string",
            "enum": [
              "Sales",
              "Support",
              "Partnership",
              "Press",
              "Other"
            ]
          },
          "message": {
            "type": "string",
            "maxLength": 2000
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Omit for a notification."
          },
          "method": {
            "type": "string",
            "examples": [
              "initialize",
              "notifications/initialized",
              "tools/list",
              "tools/call",
              "resources/list",
              "resources/read"
            ]
          },
          "params": {
            "type": "object"
          }
        }
      }
    },
    "responses": {
      "NotFound": {
        "description": "Nothing at that path. Send `Accept: application/json` for this JSON body or `Accept: text/markdown` for a markdown one; under /api/* the body is JSON unless the request asks for markdown.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "text/markdown": {
            "schema": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}