{
  "openapi": "3.1.0",
  "info": {
    "title": "The Chicagoland Lawyer API",
    "version": "1.1.0",
    "summary": "Public, unauthenticated REST API for an Illinois law firm.",
    "description": "Public REST API for **The Chicagoland Lawyer** (Abdilla and Associates, an Illinois law firm).\n\nThe API publishes the firm's flat fee schedule, practice areas, free Illinois legal document packs, and the two intake endpoints the site's own forms use.\n\n**Authentication: none.** There is no registration, no API key, and no quota to apply for. The access policy is published at https://thechicagolandlawyer.com/auth.md.\n\n**Sandbox.** Every write endpoint has a sandbox twin under `/api/v1/sandbox/`. It runs the same validation and returns the same response shape, but sends no email and stores nothing. Point an integration at the sandbox until it works, then drop `sandbox/` from the path.\n\n**Errors** are RFC 9457 `application/problem+json` with a stable machine-readable `code`, a human-readable `detail`, and a `hint` describing the fix.\n\n**Rate limits** are published on every response with RFC 9331 `RateLimit` and `RateLimit-Policy` headers, plus the widely deployed `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset` triplet.\n\n**Versioning.** The major version is in the URL path (`/api/v1/`). Breaking changes ship as `/api/v2/`. A version is retired only after carrying `Deprecation` (RFC 9745) and `Sunset` (RFC 8594) headers for at least 180 days. The machine-readable policy is at https://thechicagolandlawyer.com/api/versions.json.\n\nNothing returned by this API is legal advice, and calling it does not create an attorney-client relationship.",
    "termsOfService": "https://thechicagolandlawyer.com/terms-and-conditions/",
    "contact": {
      "name": "Abdilla and Associates",
      "url": "https://thechicagolandlawyer.com/developers/",
      "email": "justin@thechicagolandlawyer.com"
    },
    "license": {
      "name": "Free to use with attribution to Abdilla and Associates",
      "url": "https://thechicagolandlawyer.com/terms-and-conditions/"
    },
    "x-versioning": {
      "scheme": "url-path",
      "current": "v1",
      "policy": "https://thechicagolandlawyer.com/api/versions.json",
      "deprecation": "Deprecation (RFC 9745) and Sunset (RFC 8594) headers, plus Link rel=\"successor-version\", at least 180 days before removal."
    },
    "x-mcp-server": {
      "endpoint": "https://thechicagolandlawyer.com/mcp",
      "transport": "streamable-http",
      "manifest": "https://thechicagolandlawyer.com/.well-known/mcp.json"
    }
  },
  "externalDocs": {
    "description": "Developer portal: quickstart, error codes, rate limits, MCP server, CLI",
    "url": "https://thechicagolandlawyer.com/developers/"
  },
  "servers": [
    {
      "url": "https://thechicagolandlawyer.com",
      "description": "Production. Prefix paths with /api/v1/."
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "Public data",
      "description": "Read-only endpoints. No side effects, no personal data."
    },
    {
      "name": "Lead capture",
      "description": "Write endpoints that contact the firm on a person's behalf. Call these only with that person's explicit consent."
    },
    {
      "name": "Sandbox",
      "description": "Same contracts, no side effects. Nothing is emailed or stored."
    },
    {
      "name": "Legacy",
      "description": "Unversioned form endpoints that predate v1. Supported; superseded by v1."
    }
  ],
  "paths": {
    "/api/health.php": {
      "get": {
        "security": [],
        "tags": [
          "Legacy"
        ],
        "operationId": "getHealthLegacy",
        "summary": "Health check (legacy)",
        "description": "Origin health endpoint referenced by /.well-known/api-catalog. Superseded by /api/v1/health; the response carries Link rel=\"successor-version\". Still supported: the site's own forms post to it.",
        "responses": {
          "200": {
            "description": "The origin is up",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "service": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/intake.php": {
      "post": {
        "security": [],
        "tags": [
          "Legacy"
        ],
        "operationId": "submitIntakeLegacy",
        "summary": "Identity protection intake (legacy form endpoint)",
        "description": "Form-encoded endpoint used by the site's own intake forms. Superseded by /api/v1/intake; the response carries Link rel=\"successor-version\". Still supported: the site's own forms post to it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/IntakeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Intake received",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/notice-request.php": {
      "post": {
        "security": [],
        "tags": [
          "Legacy"
        ],
        "operationId": "requestDocumentPackLegacy",
        "summary": "Document pack request (legacy form endpoint)",
        "description": "Form-encoded endpoint used by the site's own lead-magnet forms. Superseded by /api/v1/document-packs; the response carries Link rel=\"successor-version\". Still supported: the site's own forms post to it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/DocumentPackRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pack delivered",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "sandbox": {
                      "type": "boolean"
                    },
                    "pack": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "description": "Site-relative PDF path"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/": {
      "get": {
        "security": [],
        "tags": [
          "Public data"
        ],
        "operationId": "getApiIndex",
        "summary": "API index",
        "description": "Lists every endpoint in the current version, the sandbox base URL, and the rate limit policy.",
        "responses": {
          "200": {
            "description": "The endpoint index",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "service": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "release": {
                      "type": "string"
                    },
                    "authentication": {
                      "type": "string",
                      "const": "none"
                    },
                    "sandbox_base_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "rate_limit": {
                      "type": "object"
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "method": {
                            "type": "string"
                          },
                          "path": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/document-packs": {
      "get": {
        "security": [],
        "tags": [
          "Public data"
        ],
        "operationId": "listDocumentPacks",
        "summary": "Free document packs",
        "description": "The free Illinois legal form packs available for request. No email needed to read this list.",
        "responses": {
          "200": {
            "description": "The available packs",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "packs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "enum": [
                              "notices",
                              "no-lease",
                              "business-exit"
                            ]
                          },
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "request": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      },
      "post": {
        "security": [],
        "tags": [
          "Lead capture"
        ],
        "operationId": "requestDocumentPack",
        "summary": "Request a free document pack",
        "description": "Emails a pack of free Illinois legal form PDFs and returns the download links. The firm follows up at that address, so call this only with the person's explicit consent. Test against /api/v1/sandbox/document-packs first.",
        "requestBody": {
          "required": true,
          "description": "Send JSON or form encoding; both are accepted.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DocumentPackRequest"
              },
              "example": {
                "email": "you@example.com",
                "pack": "notices",
                "source": "/eviction-resources/"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/DocumentPackRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The pack was sent; the response lists the files",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "sandbox": {
                      "type": "boolean"
                    },
                    "pack": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "description": "Site-relative PDF path"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/api/v1/fees": {
      "get": {
        "security": [],
        "tags": [
          "Public data"
        ],
        "operationId": "getFeeSchedule",
        "summary": "Published flat fee schedule",
        "description": "Every flat fee the firm publishes: closings, evictions by county, and LLC formation. Court filing fees and third-party costs are additional.",
        "responses": {
          "200": {
            "description": "The published fee schedule",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "fees"
                  ],
                  "properties": {
                    "currency": {
                      "type": "string",
                      "const": "USD"
                    },
                    "disclaimer": {
                      "type": "string"
                    },
                    "updated": {
                      "type": "string",
                      "format": "date"
                    },
                    "fees": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "service",
                          "jurisdiction",
                          "amount",
                          "currency",
                          "unit"
                        ],
                        "properties": {
                          "service": {
                            "type": "string",
                            "description": "The legal service the fee covers"
                          },
                          "jurisdiction": {
                            "type": "string",
                            "description": "Where the fee applies"
                          },
                          "amount": {
                            "type": "number",
                            "description": "Flat fee in USD. 0 means no fee is charged to the client."
                          },
                          "amount_max": {
                            "type": "number",
                            "description": "Present when the fee is a range; amount is then the low end."
                          },
                          "currency": {
                            "type": "string",
                            "const": "USD"
                          },
                          "unit": {
                            "type": "string",
                            "example": "flat fee"
                          },
                          "notes": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri",
                            "description": "Page describing the service"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "security": [],
        "tags": [
          "Public data"
        ],
        "operationId": "getHealth",
        "summary": "Liveness check",
        "description": "Returns ok when the edge API is serving. Cheap; safe to poll.",
        "responses": {
          "200": {
            "description": "The service is up",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "ok"
                    },
                    "service": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "release": {
                      "type": "string"
                    },
                    "sandbox": {
                      "type": "boolean"
                    },
                    "documentation": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                },
                "example": {
                  "status": "ok",
                  "service": "thechicagolandlawyer.com API",
                  "version": "v1",
                  "release": "1.1.0",
                  "sandbox": false,
                  "documentation": "https://thechicagolandlawyer.com/developers/"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/intake": {
      "post": {
        "security": [],
        "tags": [
          "Lead capture"
        ],
        "operationId": "submitIdentityProtectionIntake",
        "summary": "Submit an identity protection intake",
        "description": "Sends an identity protection inquiry to the firm: impersonation, stolen photos, deepfakes, BIPA, or false reviews. Call this only with the person's explicit consent; the firm contacts them directly. Submitting an intake does not create an attorney-client relationship.",
        "requestBody": {
          "required": true,
          "description": "Send JSON or form encoding; both are accepted.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntakeRequest"
              },
              "example": {
                "name": "Jane Doe",
                "phone": "630-555-0100",
                "email": "jane@example.com",
                "description": "Someone is using my photos on a fake dating profile."
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/IntakeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The intake reached the firm",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "sandbox": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    },
                    "disclaimer": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/api/v1/practice-areas": {
      "get": {
        "security": [],
        "tags": [
          "Public data"
        ],
        "operationId": "listPracticeAreas",
        "summary": "Practice areas",
        "description": "The firm's practice areas, each with the page that describes it and the Agent Skill that teaches an agent to handle it.",
        "responses": {
          "200": {
            "description": "The practice area list",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "practice_areas": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "format": "uri"
                          },
                          "skill": {
                            "type": "string",
                            "format": "uri"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/resources": {
      "get": {
        "security": [],
        "tags": [
          "Public data"
        ],
        "operationId": "listAgentResources",
        "summary": "Machine-readable resources for agents",
        "description": "Every agent-facing artifact this site publishes: llms.txt, the auth policy, the API catalog, this spec, the MCP server, the Agent Skills index, the CLI, and the sitemap.",
        "responses": {
          "200": {
            "description": "The resource index",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/sandbox/": {
      "get": {
        "security": [],
        "tags": [
          "Sandbox"
        ],
        "operationId": "getSandboxIndex",
        "summary": "Sandbox index",
        "description": "The sandbox base URL. It returns the same endpoint index as /api/v1/, with \"sandbox\": true, so an integration can confirm which surface it is pointed at.",
        "responses": {
          "200": {
            "description": "The sandbox endpoint index",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sandbox": {
                      "type": "boolean",
                      "const": true
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/sandbox/document-packs": {
      "post": {
        "security": [],
        "tags": [
          "Sandbox"
        ],
        "operationId": "requestDocumentPackSandbox",
        "summary": "Request a document pack (sandbox)",
        "description": "Identical validation and response shape to POST /api/v1/document-packs. No email is sent and nothing is stored. Responses carry \"sandbox\": true.",
        "requestBody": {
          "required": true,
          "description": "Send JSON or form encoding; both are accepted.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DocumentPackRequest"
              },
              "example": {
                "email": "you@example.com",
                "pack": "notices",
                "source": "/eviction-resources/"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/DocumentPackRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox accepted the request",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "sandbox": {
                      "type": "boolean"
                    },
                    "pack": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string",
                            "description": "Site-relative PDF path"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/sandbox/health": {
      "get": {
        "security": [],
        "tags": [
          "Sandbox"
        ],
        "operationId": "getHealthSandbox",
        "summary": "Liveness check (sandbox)",
        "description": "The sandbox twin of /api/v1/health. Use it to confirm an integration is pointed at the sandbox base URL.",
        "responses": {
          "200": {
            "description": "The sandbox is up",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "ok"
                    },
                    "sandbox": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/v1/sandbox/intake": {
      "post": {
        "security": [],
        "tags": [
          "Sandbox"
        ],
        "operationId": "submitIntakeSandbox",
        "summary": "Submit an intake (sandbox)",
        "description": "Identical validation and response shape to POST /api/v1/intake. No email is sent, no matter is opened, and no attorney-client relationship is formed.",
        "requestBody": {
          "required": true,
          "description": "Send JSON or form encoding; both are accepted.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntakeRequest"
              },
              "example": {
                "name": "Jane Doe",
                "phone": "630-555-0100",
                "email": "jane@example.com",
                "description": "Someone is using my photos on a fake dating profile."
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/IntakeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sandbox accepted the intake",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "sandbox": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    },
                    "disclaimer": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/versions.json": {
      "get": {
        "security": [],
        "tags": [
          "Public data"
        ],
        "operationId": "getApiVersions",
        "summary": "Version and deprecation policy",
        "description": "Machine-readable statement of which versions exist, which is current, and how deprecation is signalled.",
        "responses": {
          "200": {
            "description": "The version policy",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "current": {
                      "type": "string"
                    },
                    "release": {
                      "type": "string"
                    },
                    "scheme": {
                      "type": "string"
                    },
                    "breaking_change_policy": {
                      "type": "string"
                    },
                    "deprecation_policy": {
                      "type": "string"
                    },
                    "versions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "version": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "current",
                              "supported",
                              "deprecated",
                              "retired"
                            ]
                          },
                          "base_url": {
                            "type": "string"
                          },
                          "deprecated": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date"
                          },
                          "sunset": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {},
    "headers": {
      "RateLimit": {
        "description": "RFC 9331 quota state for the current window, e.g. \"api\";r=118;t=41.",
        "schema": {
          "type": "string"
        },
        "example": "\"api\";r=118;t=41"
      },
      "RateLimit-Policy": {
        "description": "RFC 9331 policy declaration: 120 requests per 60 seconds per client IP.",
        "schema": {
          "type": "string"
        },
        "example": "\"api\";q=120;w=60"
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying. Sent on 429.",
        "schema": {
          "type": "integer"
        },
        "example": 37
      },
      "API-Version": {
        "description": "The API version that served this response.",
        "schema": {
          "type": "string"
        },
        "example": "v1"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request was rejected before anything happened. `code` is one of missing_field, invalid_email, unknown_pack, invalid_body, or malformed_body; `errors` names the offending fields.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "NotFound": {
        "description": "No endpoint is published at that path. `code` is unknown_endpoint and `known_endpoints` lists what does exist.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "MethodNotAllowed": {
        "description": "The endpoint exists but not for this method. `code` is method_not_allowed and the Allow header lists the methods that work.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "RateLimited": {
        "description": "The rate limit was exceeded. `code` is rate_limited; wait for Retry-After seconds and retry.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "ServerError": {
        "description": "Something failed inside the API. `code` is internal_error. Retry once, then call (630) 839-9195.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "UpstreamError": {
        "description": "The mail-sending origin refused the request. `code` is upstream_error. Retry once, then call (630) 839-9195.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 Problem Details. Every 4xx and 5xx response on this API uses this shape with the media type application/problem+json.",
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "Stable URI identifying the error class; resolves to its entry in the developer portal."
          },
          "title": {
            "type": "string",
            "description": "Short human-readable summary of the error class."
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status code, repeated in the body."
          },
          "detail": {
            "type": "string",
            "description": "What went wrong with this specific request."
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code. Branch on this, not on the message.",
            "enum": [
              "missing_field",
              "invalid_email",
              "unknown_pack",
              "invalid_body",
              "malformed_body",
              "unknown_endpoint",
              "method_not_allowed",
              "rate_limited",
              "upstream_error",
              "internal_error"
            ]
          },
          "hint": {
            "type": "string",
            "description": "What to change to make the request succeed."
          },
          "documentation": {
            "type": "string",
            "format": "uri"
          },
          "errors": {
            "type": "array",
            "description": "Field-level detail, present on validation failures.",
            "items": {
              "type": "object",
              "required": [
                "field",
                "code",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "known_endpoints": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "Present on unknown_endpoint: the endpoints that do exist."
          },
          "retry_after_seconds": {
            "type": "integer",
            "description": "Present on rate_limited."
          },
          "error": {
            "type": "string",
            "description": "Deprecated alias of detail, kept because the site's own form JavaScript reads it. New integrations should read detail and code."
          }
        },
        "example": {
          "type": "https://thechicagolandlawyer.com/developers/errors/#invalid_email",
          "title": "Invalid email address",
          "status": 400,
          "detail": "The value of \"email\" is not a valid email address.",
          "code": "invalid_email",
          "hint": "Example: {\"email\": \"you@example.com\"}.",
          "documentation": "https://thechicagolandlawyer.com/developers/",
          "errors": [
            {
              "field": "email",
              "code": "invalid_email",
              "message": "Provide a deliverable email address."
            }
          ],
          "error": "The value of \"email\" is not a valid email address."
        }
      },
      "DocumentPackRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Where to send the pack. Requires the person's consent."
          },
          "pack": {
            "type": "string",
            "enum": [
              "notices",
              "no-lease",
              "business-exit"
            ],
            "default": "notices",
            "description": "Which pack to send. GET /api/v1/document-packs lists them."
          },
          "source": {
            "type": "string",
            "description": "Path the request came from, e.g. /eviction-resources/."
          }
        }
      },
      "IntakeRequest": {
        "type": "object",
        "required": [
          "name",
          "phone",
          "email"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Full name of the person seeking help"
          },
          "phone": {
            "type": "string",
            "description": "Contact phone number"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Contact email address"
          },
          "description": {
            "type": "string",
            "description": "One paragraph: what happened, where the content appears, what outcome they want"
          }
        }
      }
    }
  }
}
