{
  "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 & 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 & Associates",
      "url": "https://thechicagolandlawyer.com/developers/",
      "email": "justin@thechicagolandlawyer.com"
    },
    "license": {
      "name": "Free to use with attribution to Abdilla & 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"
          }
        }
      }
    }
  }
}
