{
  "openapi": "3.1.0",
  "info": {
    "title": "Spatial.Properties API",
    "description": "REST API for the Spatial.Properties geospatial assessment platform.",
    "version": "1.0.0"
  },
  "paths": {
    "/health": {
      "get": {
        "summary": "Health",
        "description": "Return health status and number of loaded packs.",
        "operationId": "health_health_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/chat": {
      "post": {
        "summary": "Chat",
        "description": "Stream agent responses as Server-Sent Events.\n\nAccepts a natural language question, runs it through the Claude agent\nwith spatial tools, and streams text tokens and tool call events back\nto the client.\n\nThe X-Session-Id response header contains the session ID for follow-up\nrequests (conversation continuity).",
        "operationId": "chat_chat_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "SSE event stream",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/geocode": {
      "get": {
        "summary": "Geocode",
        "description": "Resolve a suburb name to a geographic bounding box.",
        "operationId": "geocode_geocode_get",
        "parameters": [
          {
            "name": "suburb",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "title": "Suburb"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/batch/upload": {
      "post": {
        "tags": [
          "batch"
        ],
        "summary": "Batch Upload",
        "description": "Upload a CSV file for batch feasibility scoring.\n\nAccepts CSV with lat/lon or lot_number columns. Auto-detects column\ntype. Streams per-site scoring progress and results via SSE.\n\nChecks credit balance against 0.1 * row_count after CSV parse.\nRejects CSVs with more than 50 rows.\n\n**Streaming:** This endpoint returns `text/event-stream` (Server-Sent Events). The response is a stream of SSE events, not a single JSON body.",
        "operationId": "batch_upload_batch_upload_post",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_batch_upload_batch_upload_post"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "SSE event stream",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/billing/credits/balance": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Get Credit Balance",
        "description": "Get the current credit balance for the authenticated user's organization.\n\nThis is the primary billing endpoint -- Phase 13 success criteria #3.\nAny authenticated user can check their org's credit balance.",
        "operationId": "get_credit_balance_billing_credits_balance_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditBalance"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/billing/org": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Get Org Info",
        "description": "Get the authenticated user's organization details.\n\nReturns org info including name, type, balance, and metadata.",
        "operationId": "get_org_info_billing_org_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrgInfo"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/billing/credits/transactions": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Get Credit Transactions",
        "description": "Get recent credit transactions for the authenticated user's organization.\n\nReturns up to `limit` transactions, most recent first.",
        "operationId": "get_credit_transactions_billing_credits_transactions_get",
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "default": 20,
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CreditTransaction"
                  },
                  "title": "Response Get Credit Transactions Billing Credits Transactions Get"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/billing/credits/usage-history": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Get Usage History",
        "description": "Paginated credit transaction history for usage history page.\n\nReturns transactions newest-first with pagination metadata.",
        "operationId": "get_usage_history_billing_credits_usage_history_get",
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1,
              "title": "Page"
            }
          },
          {
            "name": "page_size",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 100,
              "minimum": 1,
              "default": 25,
              "title": "Page Size"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageHistoryResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/billing/org/members": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Get Org Members",
        "description": "List all members of the authenticated user's organization.\n\nReturns enriched member list with email and display name.\nRequires owner or admin role (ORG-02 role enforcement).\nReturns 403 for users with 'member' role.",
        "operationId": "get_org_members_billing_org_members_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/MemberResponse"
                  },
                  "type": "array",
                  "title": "Response Get Org Members Billing Org Members Get"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/billing/org/members/{member_id}/role": {
      "put": {
        "tags": [
          "billing"
        ],
        "summary": "Update Member Role",
        "description": "Change a member's role within the organization.\n\nRequires owner or admin role. Cannot change the owner's role.",
        "operationId": "update_member_role_billing_org_members__member_id__role_put",
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "parameters": [
          {
            "name": "member_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Member Id"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemberRoleUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/billing/org/members/{member_id}": {
      "delete": {
        "tags": [
          "billing"
        ],
        "summary": "Remove Member",
        "description": "Remove a member from the organization.\n\nRequires owner or admin role. Cannot remove the owner or yourself.",
        "operationId": "remove_member_billing_org_members__member_id__delete",
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "parameters": [
          {
            "name": "member_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Member Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/billing/invite": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Create Invite",
        "description": "Send an invitation to join the organization.\n\nRequires owner or admin role. Creates invitation record and sends\ninvite email via Supabase auth admin API.",
        "operationId": "create_invite_billing_invite_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InviteCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InviteResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/billing/invites": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "List Invites",
        "description": "List all pending invitations for the organization.\n\nRequires owner or admin role. Returns non-expired pending invites.",
        "operationId": "list_invites_billing_invites_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/InviteResponse"
                  },
                  "type": "array",
                  "title": "Response List Invites Billing Invites Get"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/billing/invite/{invite_id}": {
      "delete": {
        "tags": [
          "billing"
        ],
        "summary": "Revoke Invite",
        "description": "Revoke a pending invitation.\n\nRequires owner or admin role. Returns 404 if invite not found or\nalready processed.",
        "operationId": "revoke_invite_billing_invite__invite_id__delete",
        "security": [
          {
            "HTTPBearer": []
          }
        ],
        "parameters": [
          {
            "name": "invite_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Invite Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/billing/checkout": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Create Checkout",
        "description": "Create a Stripe Checkout Session for subscription purchase.\n\nRequires billing admin (owner/admin role). Resolves the selected tier\nto a Stripe Price ID, creates a Stripe Customer if needed, and returns\nthe Checkout Session URL for redirect.",
        "operationId": "create_checkout_billing_checkout_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/billing/portal": {
      "post": {
        "tags": [
          "billing"
        ],
        "summary": "Create Portal",
        "description": "Create a Stripe Customer Portal session for subscription management.\n\nRequires billing admin. Returns the portal URL for redirect.\nThe user can manage their subscription, update payment methods, etc.",
        "operationId": "create_portal_billing_portal_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PortalResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/billing/tiers": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Get Tiers",
        "description": "Get available subscription tiers with pricing and features.\n\nAny authenticated user can view tiers. Returns tier info without\nStripe Price IDs (those are internal).",
        "operationId": "get_tiers_billing_tiers_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/TierInfo"
                  },
                  "type": "array",
                  "title": "Response Get Tiers Billing Tiers Get"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/billing/subscription": {
      "get": {
        "tags": [
          "billing"
        ],
        "summary": "Get Subscription",
        "description": "Get the current subscription status for the authenticated user's org.\n\nReturns subscription details if active, or null fields if no\nsubscription exists.",
        "operationId": "get_subscription_billing_subscription_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionInfo"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/report/generate": {
      "post": {
        "tags": [
          "report"
        ],
        "summary": "Generate Report",
        "description": "Generate a PDF feasibility report from scoring results.\n\nAccepts scoring data and parcel geometries (which may be Point centroids\nas fallbacks). Resolves actual parcel Polygon geometries via DuckDB\ncadastre layer lookup. Streams progress events via SSE and delivers\nthe final PDF as base64 in the report_complete event.\n\n**Streaming:** This endpoint returns `text/event-stream` (Server-Sent Events). The response is a stream of SSE events, not a single JSON body.",
        "operationId": "generate_report_report_generate_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReportGenerateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "SSE event stream",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    },
    "/auth/post-signup": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Post Signup",
        "description": "Post-signup orchestration: create org + register Brevo contact.\n\nCalled once by the frontend after email verification succeeds.\nIdempotent: safe to call multiple times (org creation checks for existing org,\nBrevo contact uses update_enabled=True).\n\nReturns:\n    200 with org info on success.",
        "operationId": "post_signup_auth_post_signup_post",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        },
        "security": [
          {
            "HTTPBearer": []
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "Body_batch_upload_batch_upload_post": {
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "title": "File"
          },
          "pack_id": {
            "type": "string",
            "title": "Pack Id",
            "default": ""
          }
        },
        "type": "object",
        "required": [
          "file"
        ],
        "title": "Body_batch_upload_batch_upload_post"
      },
      "ChatRequest": {
        "properties": {
          "question": {
            "type": "string",
            "title": "Question"
          },
          "session_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Session Id"
          },
          "bbox": {
            "anyOf": [
              {
                "items": {
                  "type": "number"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Bbox"
          }
        },
        "type": "object",
        "required": [
          "question"
        ],
        "title": "ChatRequest",
        "description": "Request body for POST /chat endpoint."
      },
      "CheckoutRequest": {
        "properties": {
          "tier_id": {
            "type": "string",
            "title": "Tier Id"
          }
        },
        "type": "object",
        "required": [
          "tier_id"
        ],
        "title": "CheckoutRequest",
        "description": "Request body for POST /billing/checkout."
      },
      "CheckoutResponse": {
        "properties": {
          "checkout_url": {
            "type": "string",
            "title": "Checkout Url"
          }
        },
        "type": "object",
        "required": [
          "checkout_url"
        ],
        "title": "CheckoutResponse",
        "description": "Response body for POST /billing/checkout."
      },
      "CreditBalance": {
        "properties": {
          "org_id": {
            "type": "string",
            "title": "Org Id"
          },
          "balance": {
            "type": "number",
            "title": "Balance"
          },
          "org_name": {
            "type": "string",
            "title": "Org Name"
          }
        },
        "type": "object",
        "required": [
          "org_id",
          "balance",
          "org_name"
        ],
        "title": "CreditBalance",
        "description": "Lightweight credit balance response for the /credits/balance endpoint."
      },
      "CreditTransaction": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "org_id": {
            "type": "string",
            "title": "Org Id"
          },
          "user_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "User Id"
          },
          "amount": {
            "type": "number",
            "title": "Amount"
          },
          "balance_after": {
            "type": "number",
            "title": "Balance After"
          },
          "type": {
            "type": "string",
            "title": "Type"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "expires_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Expires At"
          },
          "related_entity_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Related Entity Id"
          },
          "action_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Action Type"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At"
          }
        },
        "type": "object",
        "required": [
          "id",
          "org_id",
          "amount",
          "balance_after",
          "type",
          "created_at"
        ],
        "title": "CreditTransaction",
        "description": "A single entry in the append-only credit ledger."
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "InviteCreate": {
        "properties": {
          "email": {
            "type": "string",
            "title": "Email"
          },
          "role": {
            "type": "string",
            "enum": [
              "admin",
              "member"
            ],
            "title": "Role",
            "default": "member"
          }
        },
        "type": "object",
        "required": [
          "email"
        ],
        "title": "InviteCreate",
        "description": "Request body for POST /billing/invite."
      },
      "InviteResponse": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "org_id": {
            "type": "string",
            "title": "Org Id"
          },
          "email": {
            "type": "string",
            "title": "Email"
          },
          "role": {
            "type": "string",
            "title": "Role"
          },
          "invited_by": {
            "type": "string",
            "title": "Invited By"
          },
          "status": {
            "type": "string",
            "title": "Status"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "title": "Expires At"
          }
        },
        "type": "object",
        "required": [
          "id",
          "org_id",
          "email",
          "role",
          "invited_by",
          "status",
          "created_at",
          "expires_at"
        ],
        "title": "InviteResponse",
        "description": "Response body for invite operations."
      },
      "MemberResponse": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "org_id": {
            "type": "string",
            "title": "Org Id"
          },
          "user_id": {
            "type": "string",
            "title": "User Id"
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ],
            "title": "Role"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At"
          },
          "email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Email"
          },
          "display_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Display Name"
          }
        },
        "type": "object",
        "required": [
          "id",
          "org_id",
          "user_id",
          "role",
          "created_at"
        ],
        "title": "MemberResponse",
        "description": "Enriched org member with user details from auth.users."
      },
      "MemberRoleUpdate": {
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "admin",
              "member"
            ],
            "title": "Role"
          }
        },
        "type": "object",
        "required": [
          "role"
        ],
        "title": "MemberRoleUpdate",
        "description": "Request body for PUT /billing/org/members/{member_id}/role."
      },
      "OrgInfo": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "org_type": {
            "type": "string",
            "enum": [
              "personal",
              "team"
            ],
            "title": "Org Type",
            "default": "personal"
          },
          "slug": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Slug"
          },
          "credit_balance": {
            "type": "number",
            "title": "Credit Balance"
          },
          "stripe_customer_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stripe Customer Id"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At"
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "credit_balance",
          "created_at"
        ],
        "title": "OrgInfo",
        "description": "Organization details including cached credit balance."
      },
      "ParcelGeometry": {
        "properties": {
          "type": {
            "type": "string",
            "title": "Type"
          },
          "coordinates": {
            "title": "Coordinates"
          }
        },
        "type": "object",
        "required": [
          "type",
          "coordinates"
        ],
        "title": "ParcelGeometry",
        "description": "GeoJSON geometry for a parcel. Accepts Point (centroid fallback) or Polygon/MultiPolygon."
      },
      "PortalResponse": {
        "properties": {
          "portal_url": {
            "type": "string",
            "title": "Portal Url"
          }
        },
        "type": "object",
        "required": [
          "portal_url"
        ],
        "title": "PortalResponse",
        "description": "Response body for POST /billing/portal."
      },
      "ReportGenerateRequest": {
        "properties": {
          "scores": {
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "type": "array",
            "title": "Scores"
          },
          "parcel_geojsons": {
            "items": {
              "$ref": "#/components/schemas/ParcelGeometry"
            },
            "type": "array",
            "title": "Parcel Geojsons"
          },
          "pack_id": {
            "type": "string",
            "title": "Pack Id",
            "default": ""
          },
          "location": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Location"
          },
          "top_n": {
            "type": "integer",
            "title": "Top N",
            "default": 5
          }
        },
        "type": "object",
        "required": [
          "scores",
          "parcel_geojsons"
        ],
        "title": "ReportGenerateRequest",
        "description": "Request body for report generation."
      },
      "SubscriptionInfo": {
        "properties": {
          "status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Status"
          },
          "tier_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tier Name"
          },
          "credits_per_month": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Credits Per Month"
          },
          "current_period_end": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Current Period End"
          },
          "cancel_at_period_end": {
            "type": "boolean",
            "title": "Cancel At Period End",
            "default": false
          }
        },
        "type": "object",
        "title": "SubscriptionInfo",
        "description": "Current subscription status for GET /billing/subscription."
      },
      "TierInfo": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id"
          },
          "name": {
            "type": "string",
            "title": "Name"
          },
          "credits": {
            "type": "integer",
            "title": "Credits"
          },
          "price_cents": {
            "type": "integer",
            "title": "Price Cents"
          },
          "price_display": {
            "type": "string",
            "title": "Price Display"
          },
          "features": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Features"
          }
        },
        "type": "object",
        "required": [
          "id",
          "name",
          "credits",
          "price_cents",
          "price_display",
          "features"
        ],
        "title": "TierInfo",
        "description": "Subscription tier information for GET /billing/tiers."
      },
      "UsageHistoryResponse": {
        "properties": {
          "transactions": {
            "items": {
              "$ref": "#/components/schemas/CreditTransaction"
            },
            "type": "array",
            "title": "Transactions"
          },
          "page": {
            "type": "integer",
            "title": "Page"
          },
          "page_size": {
            "type": "integer",
            "title": "Page Size"
          },
          "total": {
            "type": "integer",
            "title": "Total"
          },
          "total_pages": {
            "type": "integer",
            "title": "Total Pages"
          }
        },
        "type": "object",
        "required": [
          "transactions",
          "page",
          "page_size",
          "total",
          "total_pages"
        ],
        "title": "UsageHistoryResponse",
        "description": "Paginated usage history response."
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          },
          "input": {
            "title": "Input"
          },
          "ctx": {
            "type": "object",
            "title": "Context"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      }
    },
    "securitySchemes": {
      "HTTPBearer": {
        "type": "http",
        "scheme": "bearer"
      }
    }
  }
}
