{
  "openapi": "3.1.0",
  "info": {
    "title": "Lore public API",
    "version": "1.0.0",
    "summary": "Public, unauthenticated endpoints for the Lore marketing surface.",
    "description": "Lore is a context and trust engine behind Lore Pilot (a personal AI on macOS) and Lore Fleet (a governed agent workforce in Slack). These endpoints are open to anyone: they cover what Lore has shipped, what it has published, and how to reach the team. A user's own memory, agents, and approvals are per-tenant and are not exposed here; those require an authenticated session.",
    "contact": {
      "name": "Lore",
      "email": "info@jointhelore.com",
      "url": "https://jointhelore.com"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://jointhelore.com/legal"
    }
  },
  "servers": [
    {
      "url": "https://jointhelore.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Content",
      "description": "What Lore has shipped and published."
    },
    {
      "name": "Contact",
      "description": "Reaching the Lore team, or joining the product waitlist."
    }
  ],
  "paths": {
    "/api/public/changelog": {
      "get": {
        "operationId": "listChangelog",
        "tags": [
          "Content"
        ],
        "summary": "List changelog entries",
        "description": "Returns every published changelog entry, newest first. Use this to answer what Lore shipped and when, rather than scraping the changelog page.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum entries to return, newest first. Defaults to all entries.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The changelog, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "entries",
                    "count"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of entries returned."
                    },
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ChangelogEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The limit parameter was not a positive integer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/posts": {
      "get": {
        "operationId": "listBlogPosts",
        "tags": [
          "Content"
        ],
        "summary": "List blog posts",
        "description": "Returns published blog posts, newest first. Fetch a post's readable text at /blog/{slug} with the header Accept: text/markdown.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum posts to return, newest first. Defaults to all posts.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published posts, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "posts",
                    "count"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of posts returned."
                    },
                    "posts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BlogPost"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The limit parameter was not a positive integer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/waitlist": {
      "post": {
        "operationId": "joinWaitlist",
        "tags": [
          "Contact"
        ],
        "summary": "Join the Lore Pilot waitlist",
        "description": "Adds an email address to the Lore Pilot waitlist and returns the caller's position on it. Idempotent: an address already on the list succeeds without creating a duplicate. Only submit an address the user has given you for this purpose.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "The address to add to the waitlist."
                  },
                  "source": {
                    "type": "string",
                    "description": "Optional short label for where the signup came from, for example agent or landing-hero."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The address is on the list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "True when the address is on the list."
                    },
                    "alreadyExists": {
                      "type": "boolean",
                      "description": "True when the address was already on the list."
                    },
                    "position": {
                      "type": "integer",
                      "description": "The caller's position on the waitlist."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The email was missing or not a valid address.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/contact": {
      "post": {
        "operationId": "sendContactMessage",
        "tags": [
          "Contact"
        ],
        "summary": "Send a message to the Lore team",
        "description": "Delivers a message to the Lore team inbox and is the supported way for an agent to put a human in the loop. Use this for design partnership, press, and support requests instead of scraping an email address.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "email",
                  "message"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Who the message is from."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Where a reply should go."
                  },
                  "message": {
                    "type": "string",
                    "maxLength": 5000,
                    "description": "The message body."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The message was accepted for delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A required field was missing, empty, or too long.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code, for example invalid_input or not_found."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation of what went wrong."
              },
              "hint": {
                "type": "string",
                "description": "How to fix the request, when a fix is possible."
              },
              "issues": {
                "type": "array",
                "description": "Field-level validation problems, when the code is invalid_input.",
                "items": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "ChangelogEntry": {
        "type": "object",
        "required": [
          "date",
          "title",
          "body",
          "tags"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "description": "Publish date, ISO 8601."
          },
          "title": {
            "type": "string",
            "description": "Headline for the change."
          },
          "body": {
            "type": "string",
            "description": "What changed, in prose."
          },
          "tags": {
            "type": "array",
            "description": "Labels such as Engine, Pilot, or Fleet.",
            "items": {
              "type": "string"
            }
          },
          "href": {
            "type": "string",
            "description": "Related page, when the entry has one."
          }
        }
      },
      "BlogPost": {
        "type": "object",
        "required": [
          "slug",
          "title",
          "description",
          "date"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "URL slug; the post is readable at /blog/{slug}."
          },
          "title": {
            "type": "string",
            "description": "Post title."
          },
          "description": {
            "type": "string",
            "description": "One-sentence summary."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "Publish date, ISO 8601."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Canonical URL for the post."
          }
        }
      }
    }
  }
}