{
  "openapi": "3.1.0",
  "info": {
    "title": "H2Office API",
    "version": "1.0.0",
    "summary": "JSON REST API for the H2Office office water delivery service.",
    "description": "The H2Office API powers the customer dashboard at https://www.h2office.io. It manages office accounts, subscription plans, water deliveries, and Stripe billing. Authentication is session-cookie based (better-auth): sign in via the auth endpoints, then send the session cookie on every request. All endpoints except the auth endpoints and the Stripe webhook require a session and return 401 without one. Responses include RateLimit headers (60 requests/minute per IP by default); a 429 response carries Retry-After. Human-readable docs: https://www.h2office.io/docs. Agent guidance: https://www.h2office.io/llms.txt.",
    "termsOfService": "https://www.h2office.io/terms",
    "contact": {
      "name": "H2Office Support",
      "email": "support@h2office.io",
      "url": "https://www.h2office.io/contact"
    }
  },
  "servers": [
    {
      "url": "https://www.h2office.io",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Auth",
      "description": "Session management (better-auth)."
    },
    {
      "name": "Account",
      "description": "The signed-in office's profile."
    },
    {
      "name": "Dashboard",
      "description": "Summary data for the dashboard."
    },
    {
      "name": "Billing",
      "description": "Plans, subscription, and Stripe billing."
    },
    {
      "name": "Deliveries",
      "description": "Scheduled and past water deliveries."
    },
    {
      "name": "Onboarding",
      "description": "Signup flow state."
    },
    {
      "name": "Places",
      "description": "Address autocomplete (Google Places proxy)."
    },
    {
      "name": "Webhooks",
      "description": "Inbound webhooks."
    }
  ],
  "security": [
    {
      "sessionCookie": []
    }
  ],
  "paths": {
    "/api/auth/sign-up/email": {
      "post": {
        "operationId": "signUpWithEmail",
        "tags": [
          "Auth"
        ],
        "summary": "Create an account with email and password",
        "description": "Creates a user account and starts a session. The session cookie is returned via Set-Cookie.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignUpRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Account created; session cookie set.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthSession"
                }
              }
            }
          },
          "400": {
            "description": "Invalid email, weak password, or email already in use.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/sign-in/email": {
      "post": {
        "operationId": "signInWithEmail",
        "tags": [
          "Auth"
        ],
        "summary": "Sign in with email and password",
        "description": "Verifies credentials and starts a session. The session cookie is returned via Set-Cookie and must be sent on subsequent requests.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignInRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed in; session cookie set.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthSession"
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/get-session": {
      "get": {
        "operationId": "getSession",
        "tags": [
          "Auth"
        ],
        "summary": "Inspect the current session",
        "description": "Returns the current session and user, or null when no valid session cookie is present.",
        "security": [],
        "responses": {
          "200": {
            "description": "The current session, or null.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthSession"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/me": {
      "get": {
        "operationId": "getCurrentUser",
        "tags": [
          "Account"
        ],
        "summary": "Get the signed-in office's profile",
        "description": "Returns the account profile: contact details, business details, delivery address, plan, and status.",
        "responses": {
          "200": {
            "description": "The account profile.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeResponse"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "updateCurrentUser",
        "tags": [
          "Account"
        ],
        "summary": "Update business and delivery details",
        "description": "Updates the account's contact name, business details, phone, and delivery address. Fields omitted from the body are left unchanged.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateMeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated profile.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeResponse"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/dashboard": {
      "get": {
        "operationId": "getDashboardSummary",
        "tags": [
          "Dashboard"
        ],
        "summary": "Dashboard summary",
        "description": "Returns the signed-in office's plan status, upcoming deliveries, and subscription summary in one call.",
        "responses": {
          "200": {
            "description": "Dashboard summary.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DashboardResponse"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/billing": {
      "get": {
        "operationId": "getBillingOverview",
        "tags": [
          "Billing"
        ],
        "summary": "Pricing catalog and current subscription",
        "description": "Returns the public pricing catalog (all tiers plus the hot/cold add-on) and, when the office has one, its current subscription including payment method summary.",
        "responses": {
          "200": {
            "description": "Billing overview.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingResponse"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/delivery": {
      "get": {
        "operationId": "listDeliveries",
        "tags": [
          "Deliveries"
        ],
        "summary": "List the office's deliveries",
        "description": "Returns every delivery for the signed-in office, newest first, including scheduled and completed deliveries.",
        "responses": {
          "200": {
            "description": "Delivery history.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveriesResponse"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/onboarding": {
      "post": {
        "operationId": "saveOnboarding",
        "tags": [
          "Onboarding"
        ],
        "summary": "Save onboarding details",
        "description": "Persists the signup flow's business, address, and plan selections ahead of checkout.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnboardingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Details saved.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "description": "A subscription checkout was requested without a valid tier.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/stripe/checkout": {
      "post": {
        "operationId": "createCheckoutSession",
        "tags": [
          "Billing"
        ],
        "summary": "Create a Stripe Checkout session",
        "description": "Creates a Stripe Checkout session for a subscription or a one-time bottle order and returns its URL. If the office already has a live subscription, a Billing Portal session for changing tiers is returned instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "URL of the Stripe-hosted page to redirect the customer to.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutResponse"
                }
              }
            }
          },
          "400": {
            "description": "Subscription checkout requested without a valid tier.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Stripe prices are not configured; retry later.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/stripe/manage": {
      "post": {
        "operationId": "manageSubscription",
        "tags": [
          "Billing"
        ],
        "summary": "Manage the subscription",
        "description": "With `action: \"toggle_addon\"`, toggles the hot/cold dispenser add-on directly on the live subscription. Otherwise opens a Stripe Billing Portal session (upgrade, downgrade, payment method, cancel) and returns its URL.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ManageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Either `{ ok: true }` for add-on toggles or `{ url }` for portal sessions.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ManageResponse"
                }
              }
            }
          },
          "400": {
            "description": "No billing account or no active subscription.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Stripe prices are not configured; retry later.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/places/autocomplete": {
      "post": {
        "operationId": "autocompleteAddress",
        "tags": [
          "Places"
        ],
        "summary": "Autocomplete a US address",
        "description": "Proxies Google Places autocomplete for US addresses; used by the signup flow. Inputs shorter than 3 characters return an empty suggestion list.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutocompleteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Address suggestions.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutocompleteResponse"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Google Places was unavailable.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Address search is not configured.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/places/details": {
      "post": {
        "operationId": "getAddressDetails",
        "tags": [
          "Places"
        ],
        "summary": "Resolve an address suggestion",
        "description": "Fetches the structured address (street, city, state, zip, coordinates) for a place ID returned by autocompleteAddress.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlaceDetailsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The resolved address.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaceDetailsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing place ID.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No valid session cookie was sent.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Wait Retry-After seconds before retrying.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Google Places was unavailable.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Address search is not configured.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/stripe/webhooks": {
      "post": {
        "operationId": "receiveStripeWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Stripe webhook receiver",
        "description": "Receives Stripe events (checkout completion, subscription lifecycle, invoice payment). Requests must carry a valid Stripe-Signature header; this endpoint is for Stripe, not API clients.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "A Stripe event object.",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid Stripe signature.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "RateLimit-Policy": {
                "description": "Rate limit policy in IETF draft format, e.g. \"api-per-ip\";q=60;w=60 (60 requests per 60-second window).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit": {
                "description": "Current rate limit state in IETF draft format, e.g. \"api-per-ip\";r=59;t=30 (59 requests remaining, window resets in 30 seconds).",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "description": "Requests allowed per window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests remaining in the current window (legacy header).",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the current window resets (legacy header).",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "h2office.session_token",
        "description": "Session cookie issued by the auth endpoints (better-auth). Obtain it via signInWithEmail or signUpWithEmail."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Standard error envelope.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message."
          }
        }
      },
      "SuccessResponse": {
        "type": "object",
        "description": "Generic success acknowledgement.",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "received": {
            "type": "boolean"
          }
        }
      },
      "SignUpRequest": {
        "type": "object",
        "required": [
          "name",
          "email",
          "password"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Contact name."
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "password": {
            "type": "string",
            "format": "password",
            "minLength": 8
          }
        }
      },
      "SignInRequest": {
        "type": "object",
        "required": [
          "email",
          "password"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "password": {
            "type": "string",
            "format": "password"
          }
        }
      },
      "AuthSession": {
        "type": "object",
        "description": "A better-auth session envelope; null when signed out.",
        "properties": {
          "session": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Session record including expiry."
          },
          "user": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "The signed-in user."
          }
        }
      },
      "UserProfile": {
        "type": "object",
        "description": "The signed-in office's profile.",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "businessName": {
            "type": [
              "string",
              "null"
            ]
          },
          "businessType": {
            "type": [
              "string",
              "null"
            ],
            "description": "One of: law, medical, dental, real-estate, insurance, tax, coworking, salon, other."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "unit": {
            "type": [
              "string",
              "null"
            ],
            "description": "Suite/office number."
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "zipCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "plan": {
            "type": [
              "string",
              "null"
            ],
            "description": "Current plan key: small, mid, or large."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Account status: created, pending, active, inactive, or suspended."
          }
        }
      },
      "MeResponse": {
        "type": "object",
        "required": [
          "user"
        ],
        "properties": {
          "user": {
            "$ref": "#/components/schemas/UserProfile"
          }
        }
      },
      "UpdateMeRequest": {
        "type": "object",
        "description": "Profile fields to update; omitted fields are unchanged.",
        "properties": {
          "name": {
            "type": "string"
          },
          "businessName": {
            "type": "string"
          },
          "businessType": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "unit": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "zipCode": {
            "type": "string"
          }
        }
      },
      "Tier": {
        "type": "object",
        "description": "A subscription plan.",
        "properties": {
          "key": {
            "type": "string",
            "enum": [
              "small",
              "mid",
              "large"
            ]
          },
          "displayName": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "monthlyPrice": {
            "type": "number",
            "description": "US dollars per month."
          },
          "bottlesPerMonth": {
            "type": "integer"
          },
          "deliveriesPerMonth": {
            "type": "integer"
          },
          "popular": {
            "type": "boolean"
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "AddOn": {
        "type": "object",
        "description": "The hot/cold dispenser add-on.",
        "properties": {
          "key": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "monthlyPrice": {
            "type": "number",
            "description": "US dollars per month."
          }
        }
      },
      "Delivery": {
        "type": "object",
        "description": "A scheduled or completed water delivery.",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "Delivery status, e.g. scheduled, delivered, cancelled."
          },
          "scheduledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "deliveryDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "bottleCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "plan": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SubscriptionSummary": {
        "type": [
          "object",
          "null"
        ],
        "description": "The office's subscription; null when none exists.",
        "properties": {
          "tierKey": {
            "type": [
              "string",
              "null"
            ]
          },
          "displayName": {
            "type": "string"
          },
          "plan": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "description": "Stripe subscription status."
          },
          "bottleCount": {
            "type": [
              "integer",
              "null"
            ]
          },
          "deliveriesPerMonth": {
            "type": [
              "integer",
              "null"
            ]
          },
          "monthlyRate": {
            "type": "number",
            "description": "US dollars per month."
          },
          "interval": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "month",
              "year",
              null
            ]
          },
          "currentPeriodEnd": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "hotColdAddOn": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "last4": {
            "type": [
              "string",
              "null"
            ],
            "description": "Card last four digits."
          },
          "brand": {
            "type": [
              "string",
              "null"
            ],
            "description": "Card brand."
          },
          "cancelAtPeriodEnd": {
            "type": [
              "boolean",
              "null"
            ]
          }
        }
      },
      "DashboardResponse": {
        "type": "object",
        "properties": {
          "user": {
            "type": "object",
            "properties": {
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "businessName": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "plan": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "bottleCount": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          },
          "upcomingDeliveries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Delivery"
            }
          },
          "subscription": {
            "$ref": "#/components/schemas/SubscriptionSummary"
          }
        }
      },
      "BillingResponse": {
        "type": "object",
        "properties": {
          "tiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Tier"
            }
          },
          "addOn": {
            "$ref": "#/components/schemas/AddOn"
          },
          "subscription": {
            "$ref": "#/components/schemas/SubscriptionSummary"
          }
        }
      },
      "DeliveriesResponse": {
        "type": "object",
        "properties": {
          "deliveries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Delivery"
            }
          }
        }
      },
      "OnboardingRequest": {
        "type": "object",
        "description": "Signup flow selections saved ahead of checkout.",
        "properties": {
          "checkoutType": {
            "type": "string",
            "enum": [
              "subscription",
              "one_time"
            ],
            "default": "subscription"
          },
          "tierKey": {
            "type": "string",
            "enum": [
              "small",
              "mid",
              "large"
            ]
          },
          "hotCold": {
            "type": "boolean",
            "default": false
          },
          "businessName": {
            "type": "string"
          },
          "businessType": {
            "type": "string"
          },
          "contactName": {
            "type": "string"
          },
          "employeeCount": {
            "type": "integer"
          },
          "officeSize": {
            "type": "string"
          },
          "hasWaitingRoom": {
            "type": "boolean"
          },
          "address": {
            "type": "string"
          },
          "unit": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "zipCode": {
            "type": "string"
          }
        }
      },
      "CheckoutRequest": {
        "type": "object",
        "description": "Checkout selections. `tierKey` is required when `checkoutType` is `subscription`.",
        "properties": {
          "checkoutType": {
            "type": "string",
            "enum": [
              "subscription",
              "one_time"
            ],
            "default": "subscription"
          },
          "tierKey": {
            "type": "string",
            "enum": [
              "small",
              "mid",
              "large"
            ]
          },
          "hotCold": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "CheckoutResponse": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Stripe-hosted Checkout or Billing Portal URL."
          }
        }
      },
      "ManageRequest": {
        "type": "object",
        "description": "Set `action: \"toggle_addon\"` with `enabled` to change the add-on; otherwise optionally pass `tierKey` to preselect a plan in the portal.",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "toggle_addon"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "tierKey": {
            "type": "string",
            "enum": [
              "small",
              "mid",
              "large"
            ]
          }
        }
      },
      "ManageResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "AutocompleteRequest": {
        "type": "object",
        "required": [
          "input"
        ],
        "properties": {
          "input": {
            "type": "string",
            "minLength": 3,
            "description": "Partial address text."
          },
          "sessionToken": {
            "type": "string",
            "description": "Optional Places session token for billing grouping."
          }
        }
      },
      "AutocompleteResponse": {
        "type": "object",
        "properties": {
          "suggestions": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "type": "object",
              "properties": {
                "placeId": {
                  "type": "string"
                },
                "text": {
                  "type": "string",
                  "description": "Full formatted suggestion."
                },
                "mainText": {
                  "type": "string",
                  "description": "Primary line (street address)."
                },
                "secondaryText": {
                  "type": "string",
                  "description": "Secondary line (city, state)."
                }
              }
            }
          }
        }
      },
      "PlaceDetailsRequest": {
        "type": "object",
        "required": [
          "placeId"
        ],
        "properties": {
          "placeId": {
            "type": "string"
          },
          "sessionToken": {
            "type": "string"
          }
        }
      },
      "PlaceDetailsResponse": {
        "type": "object",
        "required": [
          "address"
        ],
        "properties": {
          "address": {
            "type": "object",
            "description": "Structured US address parsed from Google Places.",
            "properties": {
              "address": {
                "type": "string",
                "description": "Street address line."
              },
              "unit": {
                "type": "string",
                "description": "Suite/unit, when present."
              },
              "city": {
                "type": "string"
              },
              "state": {
                "type": "string",
                "description": "Two-letter state code."
              },
              "zipCode": {
                "type": "string"
              },
              "placeId": {
                "type": "string"
              },
              "formattedAddress": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}