{
  "openapi": "3.1.0",
  "info": {
    "title": "Cronch API",
    "version": "1.0.0",
    "summary": "Plan and track a person’s workday meals, water and breaks.",
    "description": "The Cronch REST API: today’s meal, water and break plan, the profile it is built from, history, the agent and the Cronch inbox. Cronch is a personal agent that plans a person’s workday around meals, water and breaks.\n\n**Authentication.** Send a personal access token from Apps & MCP in the Cronch dashboard as `Authorization: Bearer cronch_pat_…`. Every token can read; write is a separate scope you choose when you create it, and read-only tokens get 403 on POST, PUT, PATCH and DELETE. OAuth 2.1 access tokens (scopes `cronch:read` and `cronch:write`) are for the MCP endpoint: discovery is RFC 9728 metadata at https://api.cronch.io/.well-known/oauth-protected-resource/api/v1/mcp and RFC 8414 metadata at https://api.cronch.io/.well-known/oauth-authorization-server.\n\n**Sandbox.** https://api.cronch.io/api/v1/sandbox serves the same routes with a sample day (America/New_York, seen at 12:10), no account and no token. Nothing you change there is saved.\n\n**Errors.** Every error is JSON: `{ statusCode, message, error, code? }`. `code` is stable where it exists.\n\n**Rate limits.** 60 requests a minute per client by default. Responses carry `RateLimit` and `RateLimit-Policy` (draft-ietf-httpapi-ratelimit-headers) plus `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`; a 429 adds `Retry-After` in seconds. AI requests (hungry, swap and the agent) also count toward a daily allowance, 25 a day during the beta; past it they return 402 with code `limit_reached`.\n\n**Idempotency.** POST, PUT, PATCH and DELETE accept an `Idempotency-Key` header (draft-ietf-httpapi-idempotency-key-header), for example `Idempotency-Key: \"8e03978e-40d5-43e8-bc93-6894a57f9324\"`. Keys are kept for 24 hours per access token. A retry with the same key and request gets the first response again with `Idempotent-Replayed: true`; the same key with a different request gets 422, and a retry while the first is still running gets 409.\n\n**Versioning.** The version is in the path (`/api/v1`). Within v1, changes are additive: new endpoints, new optional fields, new enum values. A breaking change would ship as `/api/v2` alongside v1, and a v1 endpoint due to go away is marked `deprecated` here and answers with `Deprecation` and `Sunset` headers (RFC 9745, RFC 8594) for at least 90 days first.\n\nDocs: https://cronch.io/docs. Support: support@cronch.io.",
    "contact": {
      "name": "Cronch support",
      "email": "support@cronch.io",
      "url": "https://cronch.io/contact"
    }
  },
  "externalDocs": {
    "description": "Developer docs",
    "url": "https://cronch.io/docs"
  },
  "servers": [
    {
      "url": "https://api.cronch.io/api/v1",
      "description": "Production. Needs a personal access token (or OAuth, at /mcp)."
    },
    {
      "url": "https://api.cronch.io/api/v1/sandbox",
      "description": "Sandbox: the same routes with sample data. No account or token, and nothing is saved."
    }
  ],
  "tags": [
    {
      "name": "Plan",
      "description": "Today’s plan and the actions on its items."
    },
    {
      "name": "Profile",
      "description": "The settings the plan is built from."
    },
    {
      "name": "Agent",
      "description": "The Cronch agent, in plain language."
    },
    {
      "name": "History",
      "description": "Streaks and how the plan was kept."
    },
    {
      "name": "Mailbox",
      "description": "The user’s Cronch inbox, where sign-in codes land."
    },
    {
      "name": "MCP",
      "description": "The Model Context Protocol server at https://api.cronch.io/api/v1/mcp."
    },
    {
      "name": "Status",
      "description": "Health."
    }
  ],
  "paths": {
    "/": {
      "get": {
        "operationId": "getApiIndex",
        "tags": [
          "Status"
        ],
        "summary": "Describe the API",
        "description": "Where the docs, this description, the auth metadata, the MCP server and the sandbox are. On the sandbox server it lists routes to try. Needs no token.",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Links to everything an integration needs.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndex"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "Status"
        ],
        "summary": "Check the API is up",
        "description": "Public liveness check: the database and the message bus. Needs no token and is not rate limited.",
        "servers": [
          {
            "url": "https://api.cronch.io/api/v1",
            "description": "Production. Needs a personal access token (or OAuth, at /mcp)."
          }
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "The API is healthy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "A dependency is down.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/plan/today": {
      "get": {
        "operationId": "getToday",
        "tags": [
          "Plan"
        ],
        "summary": "Get today’s plan",
        "description": "Every meal, water and break item for today in the user’s time zone, with its time, status and id, the next open item, progress and the meetings the plan routes around. The plan is built on first request each day.",
        "security": [
          {
            "personalAccessToken": [
              "read"
            ]
          }
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Today’s plan.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TodayPlan"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/plan/replan": {
      "post": {
        "operationId": "replanToday",
        "tags": [
          "Plan"
        ],
        "summary": "Rebuild the rest of today",
        "description": "Replans everything still ahead today and keeps what is already done. An optional note steers the new meals, for example \"lighter dinner\".",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NoteInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The rebuilt plan.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TodayPlan"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/plan/meetings": {
      "post": {
        "operationId": "setMeetings",
        "tags": [
          "Plan"
        ],
        "summary": "Set today’s meetings",
        "description": "Replaces today’s meetings with the ones in a plain-text line such as \"standup 10:00, 1:1 15:30-16:15\", then replans so meals and pings move out of them. An empty string clears them.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MeetingsInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The replanned day.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TodayPlan"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/plan/hungry": {
      "post": {
        "operationId": "getHungryOptions",
        "tags": [
          "Plan"
        ],
        "summary": "Get food options for right now",
        "description": "Up to three meals that fit the user’s diet, allergies, budget and the time of day, each with recipe steps and Uber Eats, DoorDash and Google Maps search links. Cronch never places an order. Counts as one AI request toward the daily allowance.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NoteInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Meal options.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PlanMeal"
                  },
                  "maxItems": 3
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Today’s AI allowance is used up (code limit_reached). It resets at midnight UTC.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/plan/water": {
      "post": {
        "operationId": "logWater",
        "tags": [
          "Plan"
        ],
        "summary": "Log a glass of water",
        "description": "Marks the next open water item done, or records an extra glass when every water item is done (then the response is null). Send an Idempotency-Key so a retried request is not counted twice.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "201": {
            "description": "The water item marked done, or null for an extra glass.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/PlanItem"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/plan/items/{id}/swap": {
      "post": {
        "operationId": "swapMeal",
        "tags": [
          "Plan"
        ],
        "summary": "Swap a planned meal",
        "description": "Replaces a meal item with another that still fits diet, allergies and budget. An optional note steers it, for example \"something warm\". Counts as one AI request toward the daily allowance.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "A plan item id from GET /plan/today.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NoteInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The item with its new meal.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanItem"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Today’s AI allowance is used up (code limit_reached). It resets at midnight UTC.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/plan/items/{id}/status": {
      "post": {
        "operationId": "setItemStatus",
        "tags": [
          "Plan"
        ],
        "summary": "Mark an item done or skipped",
        "description": "Marks a meal, water or break item DONE or SKIPPED. Setting the same status again is harmless.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "A plan item id from GET /plan/today.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StatusInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The updated item.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanItem"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/plan/items/{id}/snooze": {
      "post": {
        "operationId": "snoozeItem",
        "tags": [
          "Plan"
        ],
        "summary": "Snooze an item",
        "description": "Pushes an item later, 15 minutes by default. Items that already pinged get a fresh ping at the new time.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "A plan item id from GET /plan/today.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SnoozeInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The snoozed item.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanItem"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/plan/today.ics": {
      "get": {
        "operationId": "exportTodayIcs",
        "tags": [
          "Plan"
        ],
        "summary": "Download the rest of today as a calendar file",
        "description": "The open items for today as an iCalendar file, so a phone or calendar app can do the reminding.",
        "security": [
          {
            "personalAccessToken": [
              "read"
            ]
          }
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "An iCalendar file.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "text/calendar": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/profile": {
      "get": {
        "operationId": "getProfile",
        "tags": [
          "Profile"
        ],
        "summary": "Get the profile",
        "description": "Diet, allergies, budget, cooking, work and quiet hours, ping cadence, water goal and time zone. A 404 with code NO_PROFILE means the user has not finished onboarding.",
        "security": [
          {
            "personalAccessToken": [
              "read"
            ]
          }
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "The profile.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateProfile",
        "tags": [
          "Profile"
        ],
        "summary": "Replace the profile",
        "description": "Saves the whole profile and replans the rest of today around it. Send every required field; optional fields keep their value when left out.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProfileInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved profile.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/profile/calendar/test": {
      "post": {
        "operationId": "testCalendar",
        "tags": [
          "Profile"
        ],
        "summary": "Check a calendar link",
        "description": "Reads a secret iCal link (https:// or webcal://) without saving it and returns the meetings Cronch would see today.",
        "servers": [
          {
            "url": "https://api.cronch.io/api/v1",
            "description": "Production. Needs a personal access token (or OAuth, at /mcp)."
          }
        ],
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CalendarTestInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "What Cronch sees in the calendar today.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalendarTestResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/agent": {
      "post": {
        "operationId": "askAgent",
        "tags": [
          "Agent"
        ],
        "summary": "Ask the Cronch agent",
        "description": "A plain-language request, like \"move lunch after my 1pm\" or \"/hungry 10 minutes, no cooking\". The agent answers and may change today’s plan. Pass recent turns in history for context. Counts as one AI request toward the daily allowance.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The agent’s reply and what it did.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Today’s AI allowance is used up (code limit_reached). It resets at midnight UTC.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/history": {
      "get": {
        "operationId": "getHistory",
        "tags": [
          "History"
        ],
        "summary": "Get history and streaks",
        "description": "One entry per day for the last weeks, plus the current streak, the share of planned meals eaten, water per day and breaks this week.",
        "security": [
          {
            "personalAccessToken": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "weeks",
            "in": "query",
            "required": false,
            "description": "How many weeks back, 1 to 52. Default 20.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 52,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "History.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/History"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/mailbox": {
      "get": {
        "operationId": "getMailbox",
        "tags": [
          "Mailbox"
        ],
        "summary": "Get the Cronch inbox",
        "description": "The user’s Cronch email address, unread count and forwarding settings.",
        "security": [
          {
            "personalAccessToken": [
              "read"
            ]
          }
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "The inbox.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailboxSummary"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "setMailboxForwarding",
        "tags": [
          "Mailbox"
        ],
        "summary": "Turn forwarding on or off",
        "description": "Forwards new mail to the account email or a confirmed forwarding address. Forwarding stays off when there is no address to forward to.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailboxForwardInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The inbox.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailboxSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/mailbox/messages": {
      "get": {
        "operationId": "listMailMessages",
        "tags": [
          "Mailbox"
        ],
        "summary": "List inbox messages",
        "description": "Newest first, 30 a page. Page through with page until page equals meta.totalPages. Sign-in codes are included as code; show them only when the user asks.",
        "security": [
          {
            "personalAccessToken": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number, from 1.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of messages.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailMessageList"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/mailbox/messages/{id}": {
      "get": {
        "operationId": "getMailMessage",
        "tags": [
          "Mailbox"
        ],
        "summary": "Read a message",
        "description": "The full message. Reading it marks it read.",
        "security": [
          {
            "personalAccessToken": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "A message id from GET /mailbox/messages.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The message.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailMessage"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteMailMessage",
        "tags": [
          "Mailbox"
        ],
        "summary": "Delete a message",
        "description": "Deletes one message from the Cronch inbox.",
        "security": [
          {
            "personalAccessToken": [
              "write"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "A message id from GET /mailbox/messages.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OkResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is invalid. message lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used with a different request.",
            "headers": {
              "Link": {
                "description": "A describedby link to the idempotency docs.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "callMcp",
        "tags": [
          "MCP"
        ],
        "summary": "Model Context Protocol endpoint",
        "description": "The remote MCP server (Streamable HTTP, stateless, JSON responses). POST JSON-RPC 2.0 messages with Accept: application/json, text/event-stream. It exposes 17 tools; read-only tools need cronch:read and tools that change the day need cronch:write. OAuth clients discover sign-in from the WWW-Authenticate header of a 401 or from https://api.cronch.io/.well-known/oauth-protected-resource/api/v1/mcp (RFC 9728). On the sandbox server it needs no token.",
        "security": [
          {
            "oauth2": [
              "cronch:read"
            ]
          },
          {
            "personalAccessToken": [
              "read"
            ]
          }
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcMessage"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC response.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcMessage"
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, expired or revoked token.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
            "headers": {
              "WWW-Authenticate": {
                "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Wait for Retry-After seconds.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 authorization code with PKCE (S256), for the MCP endpoint. Register with a Client ID Metadata Document or RFC 7591 dynamic registration (https://api.cronch.io/api/v1/oauth/register). Tokens are bound to the resource https://api.cronch.io/api/v1/mcp (RFC 8707). Ask for the least scope you need.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://api.cronch.io/api/v1/oauth/authorize",
            "tokenUrl": "https://api.cronch.io/api/v1/oauth/token",
            "refreshUrl": "https://api.cronch.io/api/v1/oauth/token",
            "scopes": {
              "cronch:read": "Read today’s plan, the profile, history and the Cronch inbox.",
              "cronch:write": "Change the day: mark items done, skip or snooze them, log water, swap meals, add meetings, replan, edit the profile, ask the agent and manage inbox messages."
            }
          }
        }
      },
      "personalAccessToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "cronch_pat_…",
        "description": "A personal access token from Apps & MCP in the Cronch dashboard. Its scopes are read, or read and write; the role named on each operation is the scope it needs. Tokens can’t manage the account, billing or other tokens."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "A unique key for this request, ideally a UUID, as a quoted string. Retrying with the same key returns the first response instead of doing the work twice. Kept for 24 hours per access token.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 257
        },
        "example": "\"8e03978e-40d5-43e8-bc93-6894a57f9324\""
      }
    },
    "headers": {
      "RateLimit": {
        "description": "Quota left: \"default\";r=<remaining>;t=<seconds to reset>.",
        "schema": {
          "type": "string"
        },
        "example": "\"default\";r=59;t=60"
      },
      "RateLimitPolicy": {
        "description": "The quota policy: \"default\";q=<requests>;w=<window seconds>.",
        "schema": {
          "type": "string"
        },
        "example": "\"default\";q=60;w=60"
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      },
      "IdempotentReplayed": {
        "description": "\"true\" when this is the stored response to an earlier request with the same Idempotency-Key.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is invalid. message lists what to fix.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, unknown, expired or revoked token.",
        "headers": {
          "WWW-Authenticate": {
            "description": "At /mcp: Bearer challenge with resource_metadata and scope (RFC 9728, RFC 6750).",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PlanLimit": {
        "description": "Today’s AI allowance is used up (code limit_reached). It resets at midnight UTC.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The token lacks the scope (read-only tokens can’t write) or can’t call this route.",
        "headers": {
          "WWW-Authenticate": {
            "description": "At /mcp: error=\"insufficient_scope\" with the scope needed.",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found, or code NO_PROFILE when the user hasn’t finished onboarding.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "IdempotencyConflict": {
        "description": "A request with the same Idempotency-Key is still being processed. Retry after it finishes.",
        "headers": {
          "Link": {
            "description": "A describedby link to the idempotency docs.",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "IdempotencyKeyReused": {
        "description": "The Idempotency-Key was already used with a different request.",
        "headers": {
          "Link": {
            "description": "A describedby link to the idempotency docs.",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limited. Wait for Retry-After seconds.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServerError": {
        "description": "Something went wrong on our side. Retry with backoff; a write sent with an Idempotency-Key is safe to retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unavailable": {
        "description": "A dependency is down.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "ApiIndex": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "What this is: \"Cronch API\" or \"Cronch API sandbox\"."
          },
          "description": {
            "type": "string"
          },
          "documentation": {
            "type": "string",
            "description": "Developer docs.",
            "format": "uri"
          },
          "openapi": {
            "type": "string",
            "description": "This description.",
            "format": "uri"
          },
          "mcp": {
            "type": "string",
            "description": "The MCP endpoint for this server.",
            "format": "uri"
          },
          "sandbox": {
            "type": "string",
            "description": "The sandbox (production index only).",
            "format": "uri"
          },
          "production": {
            "type": "string",
            "description": "The real API (sandbox index only).",
            "format": "uri"
          },
          "authentication": {
            "type": "object",
            "description": "Token and OAuth metadata, with the scopes (production index only)."
          },
          "try": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Routes to try first (sandbox index only)."
          }
        },
        "required": [
          "name",
          "documentation",
          "openapi",
          "mcp"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "statusCode": {
            "type": "integer",
            "description": "The HTTP status, repeated."
          },
          "message": {
            "description": "What went wrong, for a person. Validation errors list each problem.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "error": {
            "type": "string",
            "description": "The status text, for example \"Bad Request\"."
          },
          "code": {
            "type": "string",
            "description": "A stable machine-readable code when there is one: NO_PROFILE, limit_reached, IDEMPOTENCY_KEY_INVALID, IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_KEY_IN_PROGRESS."
          }
        },
        "required": [
          "statusCode",
          "message"
        ]
      },
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "\"ok\".",
            "enum": [
              "ok"
            ]
          },
          "database": {
            "type": "boolean"
          },
          "nats": {
            "type": "string",
            "description": "Message bus state."
          },
          "commit": {
            "anyOf": [
              {
                "type": "string",
                "description": "Deployed commit."
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "status",
          "database"
        ]
      },
      "OrderLinks": {
        "type": "object",
        "properties": {
          "ubereats": {
            "type": "string",
            "description": "Uber Eats search.",
            "format": "uri"
          },
          "doordash": {
            "type": "string",
            "description": "DoorDash search.",
            "format": "uri"
          },
          "maps": {
            "type": "string",
            "description": "Google Maps search.",
            "format": "uri"
          }
        },
        "required": [
          "ubereats",
          "doordash",
          "maps"
        ]
      },
      "PlanMeal": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Catalog or generated meal id."
          },
          "name": {
            "type": "string",
            "description": "Dish name."
          },
          "slot": {
            "type": "string",
            "description": "Meal slot.",
            "enum": [
              "breakfast",
              "lunch",
              "dinner",
              "snack"
            ]
          },
          "minutes": {
            "type": "integer",
            "description": "Time to make it, in minutes. 0 means no cooking."
          },
          "protein": {
            "type": "integer",
            "description": "Approximate protein, in grams."
          },
          "price": {
            "type": "string",
            "description": "Price band.",
            "enum": [
              "$",
              "$$",
              "$$$"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "blurb": {
            "type": "string",
            "description": "One line about the dish."
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Short recipe steps."
          },
          "search": {
            "type": "string",
            "description": "The search the order links open."
          },
          "links": {
            "$ref": "#/components/schemas/OrderLinks"
          }
        },
        "required": [
          "id",
          "name",
          "slot",
          "minutes",
          "protein",
          "price",
          "tags",
          "blurb",
          "steps",
          "search",
          "links"
        ]
      },
      "PlanItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Item id.",
            "format": "uuid"
          },
          "at": {
            "type": "string",
            "description": "When it is due.",
            "format": "date-time"
          },
          "time": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ],
            "description": "Local time (HH:MM) in the user’s time zone."
          },
          "kind": {
            "type": "string",
            "description": "What it is.",
            "enum": [
              "MEAL",
              "WATER",
              "BREAK",
              "WIND_DOWN"
            ]
          },
          "title": {
            "type": "string",
            "description": "Title, for example \"Lunch: shawarma bowl\"."
          },
          "detail": {
            "type": "string",
            "description": "A short line of detail."
          },
          "status": {
            "type": "string",
            "description": "Where it stands. SCHEDULED and FIRED are open.",
            "enum": [
              "SCHEDULED",
              "FIRED",
              "DONE",
              "SKIPPED",
              "SNOOZED",
              "CANCELED"
            ]
          },
          "meal": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PlanMeal"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "at",
          "time",
          "kind",
          "title",
          "detail",
          "status",
          "meal"
        ]
      },
      "Meeting": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "start": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ]
          },
          "end": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ]
          }
        },
        "required": [
          "title",
          "start",
          "end"
        ]
      },
      "TodayPlan": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "description": "Local date.",
            "format": "date"
          },
          "now": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ],
            "description": "Local time now."
          },
          "timezone": {
            "type": "string",
            "description": "IANA time zone.",
            "examples": [
              "America/New_York"
            ]
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanItem"
            }
          },
          "meetings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Meeting"
            }
          },
          "next": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/PlanItem"
              },
              {
                "type": "null"
              }
            ]
          },
          "progress": {
            "type": "object",
            "properties": {
              "meals": {
                "type": "object",
                "properties": {
                  "done": {
                    "type": "integer",
                    "description": "Done so far."
                  },
                  "total": {
                    "type": "integer",
                    "description": "Planned today."
                  }
                },
                "required": [
                  "done",
                  "total"
                ]
              },
              "water": {
                "type": "object",
                "properties": {
                  "done": {
                    "type": "integer",
                    "description": "Done so far."
                  },
                  "total": {
                    "type": "integer",
                    "description": "Planned today."
                  }
                },
                "required": [
                  "done",
                  "total"
                ]
              },
              "breaks": {
                "type": "object",
                "properties": {
                  "done": {
                    "type": "integer",
                    "description": "Done so far."
                  },
                  "total": {
                    "type": "integer",
                    "description": "Planned today."
                  }
                },
                "required": [
                  "done",
                  "total"
                ]
              }
            },
            "required": [
              "meals",
              "water",
              "breaks"
            ]
          }
        },
        "required": [
          "date",
          "now",
          "timezone",
          "items",
          "meetings",
          "next",
          "progress"
        ]
      },
      "NoteInput": {
        "type": "object",
        "properties": {
          "note": {
            "type": "string",
            "description": "Optional steer, for example \"lighter\" or \"10 minutes, no cooking\".",
            "maxLength": 300
          }
        },
        "required": []
      },
      "MeetingsInput": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "description": "Today’s meetings in plain text, like \"standup 10:00, 1:1 15:30-16:15\". Empty clears them.",
            "maxLength": 2000
          }
        },
        "required": [
          "text"
        ]
      },
      "StatusInput": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "New status.",
            "enum": [
              "DONE",
              "SKIPPED"
            ]
          }
        },
        "required": [
          "status"
        ]
      },
      "SnoozeInput": {
        "type": "object",
        "properties": {
          "minutes": {
            "type": "integer",
            "description": "How long to push it, 5 to 240. Default 15.",
            "minimum": 5,
            "maximum": 240,
            "default": 15
          }
        },
        "required": []
      },
      "Diet": {
        "type": "string",
        "description": "Diet.",
        "enum": [
          "omnivore",
          "vegetarian",
          "vegan",
          "pescatarian",
          "halal",
          "kosher"
        ]
      },
      "Allergen": {
        "type": "string",
        "description": "An allergen to keep out of every meal.",
        "enum": [
          "gluten",
          "dairy",
          "nuts",
          "peanuts",
          "eggs",
          "soy",
          "shellfish",
          "fish",
          "sesame"
        ]
      },
      "Budget": {
        "type": "string",
        "description": "Meal budget.",
        "enum": [
          "$",
          "$$",
          "$$$"
        ]
      },
      "Cooking": {
        "type": "string",
        "description": "How much the user wants to cook: none, quick (about 15 minutes) or happy to cook.",
        "enum": [
          "none",
          "quick",
          "happy"
        ]
      },
      "Goal": {
        "type": "string",
        "description": "Food goal.",
        "enum": [
          "none",
          "more_protein",
          "lighter",
          "budget",
          "energy"
        ]
      },
      "Cadence": {
        "type": "integer",
        "description": "Minutes between water and break pings.",
        "enum": [
          45,
          60,
          90,
          120
        ]
      },
      "Profile": {
        "type": "object",
        "properties": {
          "diet": {
            "$ref": "#/components/schemas/Diet"
          },
          "allergies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Allergen"
            }
          },
          "budget": {
            "$ref": "#/components/schemas/Budget"
          },
          "cooking": {
            "$ref": "#/components/schemas/Cooking"
          },
          "tastes": {
            "type": "string",
            "description": "Food the user loves, free text.",
            "maxLength": 500
          },
          "goal": {
            "$ref": "#/components/schemas/Goal"
          },
          "workStart": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ]
          },
          "workEnd": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ]
          },
          "quietStart": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ]
          },
          "quietEnd": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ]
          },
          "cadenceMinutes": {
            "$ref": "#/components/schemas/Cadence"
          },
          "waterGoal": {
            "type": "integer",
            "description": "Glasses a day.",
            "minimum": 1,
            "maximum": 16
          },
          "timezone": {
            "type": "string",
            "description": "IANA time zone."
          },
          "planMeals": {
            "type": "boolean",
            "description": "Whether meals are planned."
          },
          "emailPings": {
            "type": "boolean",
            "description": "Whether pings go out by email."
          },
          "calendarConnected": {
            "type": "boolean",
            "description": "Whether a calendar link is saved. The link itself is never returned."
          }
        },
        "required": [
          "diet",
          "allergies",
          "budget",
          "cooking",
          "workStart",
          "workEnd",
          "quietStart",
          "quietEnd",
          "cadenceMinutes",
          "waterGoal",
          "timezone"
        ]
      },
      "ProfileInput": {
        "type": "object",
        "properties": {
          "diet": {
            "$ref": "#/components/schemas/Diet"
          },
          "allergies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Allergen"
            },
            "maxItems": 9
          },
          "budget": {
            "$ref": "#/components/schemas/Budget"
          },
          "cooking": {
            "$ref": "#/components/schemas/Cooking"
          },
          "tastes": {
            "anyOf": [
              {
                "type": "string",
                "description": "Food the user loves, free text.",
                "maxLength": 500
              },
              {
                "type": "null"
              }
            ]
          },
          "goal": {
            "$ref": "#/components/schemas/Goal"
          },
          "workStart": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ]
          },
          "workEnd": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ],
            "description": "Must be after workStart."
          },
          "quietStart": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ]
          },
          "quietEnd": {
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "examples": [
              "12:30"
            ]
          },
          "cadenceMinutes": {
            "$ref": "#/components/schemas/Cadence"
          },
          "waterGoal": {
            "type": "integer",
            "description": "Glasses a day.",
            "minimum": 1,
            "maximum": 16
          },
          "timezone": {
            "type": "string",
            "description": "IANA time zone, for example \"Europe/London\".",
            "maxLength": 64
          },
          "planMeals": {
            "type": "boolean"
          },
          "emailPings": {
            "type": "boolean"
          },
          "calendarUrl": {
            "anyOf": [
              {
                "type": "string",
                "description": "Secret iCal link (https:// or webcal://). null removes it.",
                "maxLength": 2048
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "diet",
          "allergies",
          "budget",
          "cooking",
          "workStart",
          "workEnd",
          "quietStart",
          "quietEnd",
          "cadenceMinutes",
          "waterGoal",
          "timezone"
        ]
      },
      "CalendarTestInput": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Secret iCal link.",
            "maxLength": 2048
          },
          "timezone": {
            "type": "string",
            "description": "IANA time zone.",
            "maxLength": 64
          }
        },
        "required": [
          "url",
          "timezone"
        ]
      },
      "CalendarTestResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "date": {
            "type": "string",
            "description": "Local date checked.",
            "format": "date"
          },
          "meetings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Meeting"
            }
          }
        },
        "required": [
          "ok",
          "date",
          "meetings"
        ]
      },
      "AgentTurn": {
        "type": "object",
        "properties": {
          "role": {
            "type": "string",
            "description": "Who said it.",
            "enum": [
              "user",
              "assistant"
            ]
          },
          "content": {
            "type": "string",
            "description": "What was said.",
            "maxLength": 2000
          }
        },
        "required": [
          "role",
          "content"
        ]
      },
      "AgentInput": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "The request.",
            "minLength": 1,
            "maxLength": 1000
          },
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentTurn"
            },
            "maxItems": 20,
            "description": "Recent turns, oldest first."
          }
        },
        "required": [
          "message"
        ]
      },
      "AgentResult": {
        "type": "object",
        "properties": {
          "reply": {
            "type": "string",
            "description": "What the agent says."
          },
          "action": {
            "type": "string",
            "description": "What it did, for example \"replanned\" or \"none\"."
          },
          "meals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanMeal"
            },
            "description": "Meal options, when the request was about food."
          }
        },
        "required": [
          "reply",
          "action"
        ]
      },
      "HistoryDay": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "description": "Local date.",
            "format": "date"
          },
          "meals": {
            "type": "object",
            "properties": {
              "done": {
                "type": "integer",
                "description": "Done so far."
              },
              "total": {
                "type": "integer",
                "description": "Planned today."
              }
            },
            "required": [
              "done",
              "total"
            ]
          },
          "water": {
            "type": "integer",
            "description": "Glasses logged."
          },
          "breaks": {
            "type": "integer",
            "description": "Breaks taken."
          },
          "level": {
            "type": "integer",
            "description": "0 to 4: how well the plan was kept.",
            "minimum": 0,
            "maximum": 4
          }
        },
        "required": [
          "date",
          "meals",
          "water",
          "breaks",
          "level"
        ]
      },
      "History": {
        "type": "object",
        "properties": {
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HistoryDay"
            }
          },
          "stats": {
            "type": "object",
            "properties": {
              "streak": {
                "type": "integer",
                "description": "Days in a row the plan was kept."
              },
              "mealsKept": {
                "type": "integer",
                "description": "Percent of planned meals eaten, 0 to 100."
              },
              "waterPerDay": {
                "type": "number",
                "description": "Average glasses a day."
              },
              "breaksThisWeek": {
                "type": "integer",
                "description": "Breaks taken this week."
              }
            },
            "required": [
              "streak",
              "mealsKept",
              "waterPerDay",
              "breaksThisWeek"
            ]
          }
        },
        "required": [
          "days",
          "stats"
        ]
      },
      "MailboxSummary": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "description": "The user’s Cronch address.",
            "format": "email"
          },
          "forward": {
            "type": "boolean",
            "description": "Whether new mail is forwarded."
          },
          "forwardTarget": {
            "anyOf": [
              {
                "type": "string",
                "description": "Where forwarded mail goes.",
                "format": "email"
              },
              {
                "type": "null"
              }
            ]
          },
          "forwardTo": {
            "anyOf": [
              {
                "type": "string",
                "description": "A confirmed forwarding address.",
                "format": "email"
              },
              {
                "type": "null"
              }
            ]
          },
          "forwardToPending": {
            "anyOf": [
              {
                "type": "string",
                "description": "A forwarding address waiting for confirmation.",
                "format": "email"
              },
              {
                "type": "null"
              }
            ]
          },
          "canForward": {
            "type": "boolean"
          },
          "awaitingVerification": {
            "anyOf": [
              {
                "type": "string",
                "description": "The account email that forwarded copies will go to once it is confirmed.",
                "format": "email"
              },
              {
                "type": "null"
              }
            ]
          },
          "unread": {
            "type": "integer",
            "description": "Unread messages."
          }
        },
        "required": [
          "address",
          "forward",
          "canForward",
          "unread"
        ]
      },
      "MailboxForwardInput": {
        "type": "object",
        "properties": {
          "forward": {
            "type": "boolean"
          }
        },
        "required": [
          "forward"
        ]
      },
      "MailMessageSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Message id.",
            "format": "uuid"
          },
          "fromAddress": {
            "type": "string"
          },
          "fromName": {
            "type": "string"
          },
          "toAddress": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "code": {
            "anyOf": [
              {
                "type": "string",
                "description": "A sign-in code found in the message. Sensitive: show it only when the user asks."
              },
              {
                "type": "null"
              }
            ]
          },
          "provider": {
            "anyOf": [
              {
                "type": "string",
                "description": "Who sent the code, when known."
              },
              {
                "type": "null"
              }
            ]
          },
          "receivedAt": {
            "type": "string",
            "description": "When it arrived.",
            "format": "date-time"
          },
          "readAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "preview": {
            "type": "string",
            "description": "The first 140 characters of the text."
          }
        },
        "required": [
          "id",
          "fromAddress",
          "fromName",
          "toAddress",
          "subject",
          "receivedAt",
          "preview"
        ]
      },
      "MailMessage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Message id.",
            "format": "uuid"
          },
          "fromAddress": {
            "type": "string"
          },
          "fromName": {
            "type": "string"
          },
          "toAddress": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "text": {
            "type": "string",
            "description": "Plain-text body."
          },
          "html": {
            "anyOf": [
              {
                "type": "string",
                "description": "HTML body, when the sender included one."
              },
              {
                "type": "null"
              }
            ]
          },
          "attachments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "filename": {
                  "type": "string"
                },
                "contentType": {
                  "type": "string"
                },
                "size": {
                  "type": "integer",
                  "description": "Bytes."
                }
              },
              "required": [
                "filename",
                "contentType",
                "size"
              ]
            }
          },
          "code": {
            "anyOf": [
              {
                "type": "string",
                "description": "A sign-in code found in the message. Sensitive: show it only when the user asks."
              },
              {
                "type": "null"
              }
            ]
          },
          "provider": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "receivedAt": {
            "type": "string",
            "format": "date-time"
          },
          "readAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "fromAddress",
          "subject",
          "text",
          "receivedAt"
        ]
      },
      "PageMeta": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer",
            "description": "This page, from 1."
          },
          "limit": {
            "type": "integer",
            "description": "Items per page."
          },
          "total": {
            "type": "integer",
            "description": "Items in all pages."
          },
          "totalPages": {
            "type": "integer",
            "description": "Number of pages."
          }
        },
        "required": [
          "page",
          "limit",
          "total",
          "totalPages"
        ]
      },
      "MailMessageList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailMessageSummary"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/PageMeta"
          }
        },
        "required": [
          "data",
          "meta"
        ]
      },
      "OkResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "ok"
        ]
      },
      "JsonRpcMessage": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "method": {
            "type": "string",
            "description": "For requests and notifications, for example \"tools/list\" or \"tools/call\"."
          },
          "params": {
            "type": "object"
          },
          "result": {
            "type": "object"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              },
              "data": {}
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "jsonrpc"
        ]
      }
    }
  }
}