{
  "openapi": "3.0.3",
  "info": {
    "title": "LovedOne public machine surface",
    "version": "1.0.0",
    "description": "lovedone.app is a static consumer landing site (no private product CRUD). This document describes the public discovery files hosted here and the real authenticated Loved One family-inbox APIs hosted on inbox.lovedone.app / api.lovedone.app.\n\nDo not expect REST resources for messages or loved ones on this host. Family data is reached through the Model Context Protocol (JSON-RPC over Streamable HTTP) after the user mints a key or completes OAuth.\n\nPublic pages on this host also negotiate `Accept: text/markdown` (acceptmarkdown.com).",
    "contact": {
      "name": "Terahertz, Inc. dba LovedOne",
      "email": "hello@lovedone.app",
      "url": "https://lovedone.app"
    },
    "license": {
      "name": "Proprietary — see Terms",
      "url": "https://lovedone.app/terms"
    }
  },
  "servers": [
    {
      "url": "https://lovedone.app",
      "description": "Marketing site and public discovery (this spec's default server)"
    },
    {
      "url": "https://api.lovedone.app",
      "description": "Authenticated MCP (JSON-RPC). Not a REST CRUD API."
    },
    {
      "url": "https://inbox.lovedone.app",
      "description": "Signed-in family inbox application"
    }
  ],
  "tags": [
    {
      "name": "Discovery",
      "description": "Unauthenticated machine-readable files on lovedone.app"
    },
    {
      "name": "Content",
      "description": "Public HTML pages; send Accept: text/markdown for a text/markdown variant"
    },
    {
      "name": "MCP",
      "description": "Authenticated family-inbox Model Context Protocol (hosted off this landing origin)"
    }
  ],
  "security": [],
  "paths": {
    "/openapi.json": {
      "get": {
        "tags": ["Discovery"],
        "summary": "This OpenAPI document",
        "operationId": "getOpenApi",
        "responses": {
          "200": {
            "description": "OpenAPI 3.0 specification",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "405": { "$ref": "#/components/responses/JsonError" },
          "500": { "$ref": "#/components/responses/JsonError" }
        }
      }
    },
    "/api/openapi.json": {
      "get": {
        "tags": ["Discovery"],
        "summary": "OpenAPI document (API alias)",
        "operationId": "getOpenApiAlias",
        "responses": {
          "200": {
            "description": "Same document as /openapi.json",
            "content": {
              "application/json": {
                "schema": { "type": "object" }
              }
            }
          },
          "404": { "$ref": "#/components/responses/JsonError" },
          "405": { "$ref": "#/components/responses/JsonError" }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "tags": ["Discovery"],
        "summary": "LLM / agent index",
        "operationId": "getLlmsTxt",
        "responses": {
          "200": {
            "description": "Plain-text agent index",
            "content": {
              "text/plain": {
                "schema": { "type": "string" }
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "tags": ["Discovery"],
        "summary": "XML sitemap of public pages",
        "operationId": "getSitemap",
        "responses": {
          "200": {
            "description": "Sitemap",
            "content": {
              "application/xml": {
                "schema": { "type": "string" }
              }
            }
          }
        }
      }
    },
    "/robots.txt": {
      "get": {
        "tags": ["Discovery"],
        "summary": "Robots exclusion file",
        "operationId": "getRobots",
        "responses": {
          "200": {
            "description": "robots.txt",
            "content": {
              "text/plain": {
                "schema": { "type": "string" }
              }
            }
          }
        }
      }
    },
    "/.well-known/mcp/server.json": {
      "get": {
        "tags": ["Discovery"],
        "summary": "MCP server manifest for app.lovedone/family-inbox",
        "operationId": "getMcpManifest",
        "responses": {
          "200": {
            "description": "MCP server metadata (canonical endpoint is api.lovedone.app/mcp/v1)",
            "content": {
              "application/json": {
                "schema": { "type": "object" }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "tags": ["Content"],
        "summary": "LovedOne homepage",
        "operationId": "getHome",
        "parameters": [
          { "$ref": "#/components/parameters/Accept" }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/NegotiatedPage" },
          "406": { "$ref": "#/components/responses/NotAcceptable" }
        }
      }
    },
    "/about": {
      "get": {
        "tags": ["Content"],
        "summary": "About LovedOne",
        "operationId": "getAbout",
        "parameters": [{ "$ref": "#/components/parameters/Accept" }],
        "responses": {
          "200": { "$ref": "#/components/responses/NegotiatedPage" },
          "406": { "$ref": "#/components/responses/NotAcceptable" }
        }
      }
    },
    "/our-brands": {
      "get": {
        "tags": ["Content"],
        "summary": "Our brands",
        "operationId": "getOurBrands",
        "parameters": [{ "$ref": "#/components/parameters/Accept" }],
        "responses": {
          "200": { "$ref": "#/components/responses/NegotiatedPage" },
          "406": { "$ref": "#/components/responses/NotAcceptable" }
        }
      }
    },
    "/getting-started": {
      "get": {
        "tags": ["Content"],
        "summary": "Setup guide",
        "operationId": "getGettingStarted",
        "parameters": [{ "$ref": "#/components/parameters/Accept" }],
        "responses": {
          "200": { "$ref": "#/components/responses/NegotiatedPage" },
          "406": { "$ref": "#/components/responses/NotAcceptable" }
        }
      }
    },
    "/for-providers": {
      "get": {
        "tags": ["Content"],
        "summary": "Provider brief",
        "operationId": "getForProviders",
        "parameters": [{ "$ref": "#/components/parameters/Accept" }],
        "responses": {
          "200": { "$ref": "#/components/responses/NegotiatedPage" },
          "406": { "$ref": "#/components/responses/NotAcceptable" }
        }
      }
    },
    "/blog": {
      "get": {
        "tags": ["Content"],
        "summary": "Blog index",
        "operationId": "getBlog",
        "parameters": [{ "$ref": "#/components/parameters/Accept" }],
        "responses": {
          "200": { "$ref": "#/components/responses/NegotiatedPage" },
          "406": { "$ref": "#/components/responses/NotAcceptable" }
        }
      }
    },
    "/privacy": {
      "get": {
        "tags": ["Content"],
        "summary": "Privacy policy",
        "operationId": "getPrivacy",
        "parameters": [{ "$ref": "#/components/parameters/Accept" }],
        "responses": {
          "200": { "$ref": "#/components/responses/NegotiatedPage" },
          "406": { "$ref": "#/components/responses/NotAcceptable" }
        }
      }
    },
    "/terms": {
      "get": {
        "tags": ["Content"],
        "summary": "Terms of service",
        "operationId": "getTerms",
        "parameters": [{ "$ref": "#/components/parameters/Accept" }],
        "responses": {
          "200": { "$ref": "#/components/responses/NegotiatedPage" },
          "406": { "$ref": "#/components/responses/NotAcceptable" }
        }
      }
    },
    "/mcp/v1": {
      "servers": [{ "url": "https://api.lovedone.app" }],
      "get": {
        "tags": ["MCP"],
        "summary": "MCP liveness / tool listing (no secrets)",
        "operationId": "getMcpProbe",
        "description": "JSON metadata about the LovedOne MCP server. Tool calls require POST + credentials.",
        "responses": {
          "200": {
            "description": "Server metadata and tool list",
            "content": {
              "application/json": {
                "schema": { "type": "object" }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["MCP"],
        "summary": "MCP JSON-RPC (Streamable HTTP)",
        "operationId": "postMcp",
        "description": "Authenticated JSON-RPC methods: initialize, tools/list, tools/call, ping.\n\nLive auth: `Authorization: Bearer sk-sand-...` minted at https://inbox.lovedone.app/settings/mcp-keys, or OAuth 2.1 + PKCE. Phase 1 API keys are not scope-gated (empty scope list = full caller access). OAuth authorization-code scopes below are advertised at inbox.lovedone.app/.well-known/oauth-authorization-server.\n\nTools and required scopes:\n- ping — no scope\n- get_user_profile — profile:read\n- list_loved_ones — care_circle:read\n- list_care_events — care_circle:read\n- invite_family_member — care_circle:write\n- create_provider_code — care_circle:write\n- reply_to_inbox_message — care_circle:write\n\nAlias: POST https://inbox.lovedone.app/api/mcp",
        "security": [
          { "LovedOneApiKey": [] },
          {
            "LovedOneOAuth": ["profile:read", "care_circle:read"]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["jsonrpc", "method"],
                "properties": {
                  "jsonrpc": { "type": "string", "enum": ["2.0"] },
                  "id": {},
                  "method": {
                    "type": "string",
                    "enum": ["initialize", "tools/list", "tools/call", "ping"]
                  },
                  "params": { "type": "object" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC result",
            "content": {
              "application/json": {
                "schema": { "type": "object" }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials. WWW-Authenticate points at RFC 9728 resource metadata.",
            "headers": {
              "WWW-Authenticate": {
                "schema": { "type": "string" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "Accept": {
        "name": "Accept",
        "in": "header",
        "required": false,
        "description": "Content negotiation (acceptmarkdown.com). text/markdown returns Markdown; text/html or */* returns the existing visual page. Responses include Vary: Accept, Accept-Encoding.",
        "schema": {
          "type": "string",
          "example": "text/markdown"
        }
      }
    },
    "securitySchemes": {
      "LovedOneApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sk-sand-",
        "description": "LovedOne MCP API key minted by a signed-in family member at https://inbox.lovedone.app/settings/mcp-keys. Send `Authorization: Bearer sk-sand-...`. Phase 1 keys store a reserved `scopes` array; an empty list means full access for that caller (not anonymous public access). Keys never grant another family's workspace."
      },
      "LovedOneOAuth": {
        "type": "oauth2",
        "description": "OAuth 2.1 authorization-code + PKCE for MCP clients. Issuer https://inbox.lovedone.app. Metadata: https://inbox.lovedone.app/.well-known/oauth-authorization-server and https://inbox.lovedone.app/.well-known/oauth-protected-resource. Public scopes match packages/auth MCP_SCOPES (profile:read, care_circle:read, care_circle:write). audit:read exists in the schema but is not offered by default (public: false).",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://inbox.lovedone.app/oauth/authorize",
            "tokenUrl": "https://inbox.lovedone.app/oauth/token",
            "refreshUrl": "https://inbox.lovedone.app/oauth/token",
            "scopes": {
              "profile:read": "See the caller's name, email, and LovedOne workspace membership. Required by get_user_profile.",
              "care_circle:read": "List loved ones and read recent care events. Required by list_loved_ones and list_care_events.",
              "care_circle:write": "Invite family, add providers, send replies, and add notes or visits on your behalf. Required by invite_family_member, create_provider_code, reply_to_inbox_message, add_visit_note, and schedule_visit.",
              "audit:read": "Read the caller's agent activity log (internal — not advertised on the authorization server by default)."
            }
          }
        }
      }
    },
    "schemas": {
      "ApiError": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message", "hint"],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine code (not_found, method_not_allowed, not_acceptable, spec_unavailable, bad_request)"
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation"
              },
              "hint": {
                "type": "string",
                "description": "What to try next"
              }
            }
          }
        }
      }
    },
    "responses": {
      "JsonError": {
        "description": "Structured JSON error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiError" }
          }
        }
      },
      "NegotiatedPage": {
        "description": "HTML for browsers; Markdown when Accept prefers text/markdown",
        "headers": {
          "Vary": {
            "schema": { "type": "string", "example": "Accept, Accept-Encoding" }
          }
        },
        "content": {
          "text/html": { "schema": { "type": "string" } },
          "text/markdown": { "schema": { "type": "string" } }
        }
      },
      "NotAcceptable": {
        "description": "No produced type satisfies Accept (including q=0 rejections)",
        "headers": {
          "Vary": {
            "schema": { "type": "string", "example": "Accept, Accept-Encoding" }
          }
        }
      }
    }
  }
}
