{
  "openapi": "3.1.0",
  "info": {
    "title": "Nemudoro public API",
    "version": "1",
    "summary": "The few read-only endpoints of nemudoro.com that anyone may call without signing in.",
    "description": "Nemudoro is a cozy focus companion (https://nemudoro.com). These endpoints serve the app's public content and its health. Everything a signed-in person does goes through the app's own API, which is not offered for other clients and is not described here.",
    "contact": { "email": "hello@nemudoro.com" }
  },
  "externalDocs": { "description": "Nemudoro's public API", "url": "https://nemudoro.com/developers" },
  "servers": [{ "url": "https://nemudoro.com" }],
  "paths": {
    "/healthz": {
      "get": {
        "operationId": "getHealth",
        "summary": "Whether the service and its database answer",
        "parameters": [
          {
            "name": "deep",
            "in": "query",
            "required": false,
            "description": "1 also checks the media store and the timer service, and names the part that failed. Rate-limited.",
            "schema": { "type": "string", "enum": ["1"] }
          }
        ],
        "responses": {
          "200": { "description": "Up", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Health" } } } },
          "503": { "description": "A part is down", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Health" } } } },
          "429": { "description": "Too many deep checks; see Retry-After" }
        }
      }
    },
    "/api/quests": {
      "get": {
        "operationId": "listQuests",
        "summary": "The one-minute wellness quests offered during a short break",
        "responses": {
          "200": {
            "description": "Every quest",
            "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Quest" } } } }
          }
        }
      }
    },
    "/api/whispers": {
      "get": {
        "operationId": "listWhispers",
        "summary": "The companion's lines, by the moment they belong to",
        "responses": {
          "200": {
            "description": "Pools of lines, keyed by moment (idle, focus, a break, paused and so on)",
            "content": {
              "application/json": {
                "schema": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string" } } }
              }
            }
          }
        }
      }
    },
    "/api/story": {
      "get": {
        "operationId": "getStory",
        "summary": "Nemudoro's story, with the stage art beside each paragraph",
        "responses": {
          "200": { "description": "The story", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Story" } } } }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Health": {
        "type": "object",
        "required": ["status"],
        "properties": {
          "status": { "type": "string", "enum": ["ok", "unavailable"] },
          "d1": { "type": "boolean", "description": "deep=1 only: the database answered" },
          "r2": { "type": "boolean", "description": "deep=1 only: the media store answered" },
          "do": { "type": "boolean", "description": "deep=1 only: the timer service answered" }
        }
      },
      "Quest": {
        "type": "object",
        "required": ["id", "name", "emoji", "short", "guidance"],
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "emoji": { "type": "string" },
          "short": { "type": "string" },
          "guidance": { "type": "string" }
        }
      },
      "Story": {
        "type": "object",
        "required": ["title", "paragraphs"],
        "properties": {
          "title": { "type": "string" },
          "paragraphs": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["text", "art"],
              "properties": {
                "text": { "type": "string" },
                "art": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": ["stage", "name", "sprite"],
                    "properties": {
                      "stage": { "type": "integer", "minimum": 1, "maximum": 10 },
                      "name": { "type": "string" },
                      "sprite": { "type": "string", "description": "A path on nemudoro.com" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
