{
  "openapi": "3.1.0",
  "info": {
    "title": "MailerDot Public API",
    "version": "1.0.0",
    "summary": "Read-only access to MailerDot city directories, archives, weather, facts, and search; one open write endpoint for newsletter signup.",
    "description": "MailerDot delivers free daily newsletters for U.S. cities. This API exposes the same data the SPA reads.\n\nAll read endpoints are anonymous, cacheable, and CORS-open (`Access-Control-Allow-Origin: *`). The single open write endpoint, `POST /api/subscribe`, accepts a city + email and triggers a welcome email plus the first daily digest.\n\n## Errors\n\nEvery failure answers with `application/problem+json` (RFC 9457) — never an HTML page, on any path under `/api/`, including unrouted ones. Branch on `code`, show `detail`, and act on `resolution`:\n\n```json\n{\n  \"type\": \"https://mailerdot.com/developers#error-city_not_found\",\n  \"title\": \"No MailerDot city for that slug\",\n  \"status\": 404,\n  \"detail\": \"No MailerDot city matches the slug \\\"atlantis-texas\\\".\",\n  \"instance\": \"/api/city/atlantis-texas\",\n  \"code\": \"city_not_found\",\n  \"resolution\": \"Fetch https://mailerdot.com/api/cities for every city with a published edition. A U.S. city with no MailerDot edition yet has no data to return; subscribing to it creates one.\",\n  \"documentation\": \"https://mailerdot.com/developers\",\n  \"error\": \"No MailerDot city matches the slug \\\"atlantis-texas\\\".\"\n}\n```\n\n`error` duplicates `detail` for older clients. `issues[]` is present on validation failures. `code` is stable — the full list is in the `ProblemCode` schema and documented at https://mailerdot.com/developers#errors.\n\nAgent discovery: developer portal at https://mailerdot.com/developers, RFC 9727 linkset at `/.well-known/api-catalog`, MCP server at `/mcp`, and `/llms.txt`.",
    "contact": {
      "name": "MailerDot",
      "email": "hello@mailerdot.com",
      "url": "https://mailerdot.com/developers"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://mailerdot.com/terms"
    }
  },
  "servers": [
    {
      "url": "https://mailerdot.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "cities",
      "description": "City directory and metadata"
    },
    {
      "name": "archives",
      "description": "Past newsletter editions"
    },
    {
      "name": "search",
      "description": "Full-text search across archives"
    },
    {
      "name": "weather",
      "description": "Daily weather snapshots"
    },
    {
      "name": "facts",
      "description": "Schema.org Dataset facts for AI consumption"
    },
    {
      "name": "subscribe",
      "description": "Newsletter signup"
    },
    {
      "name": "territories",
      "description": "City lease availability and pricing"
    },
    {
      "name": "discovery",
      "description": "Agent discovery resources"
    }
  ],
  "paths": {
    "/api/territories": {
      "get": {
        "tags": [
          "territories"
        ],
        "summary": "Check whether a city can be leased, and what it costs",
        "description": "With no query parameters, returns the cities currently leased. With `city` and `state`, returns availability and the current monthly price for that city — $10 covers the first 1,000 subscribers, then +$5 per additional 1,000. With `slugs` (comma-separated, up to 30), returns the same quote per slug in one request — the claim search prices a list of alternatives this way. Never identifies the operator holding a city.",
        "parameters": [
          {
            "name": "city",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 120
            },
            "example": "Austin"
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "example": "Texas"
          },
          {
            "name": "slugs",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 4000
            },
            "example": "austin-texas,boise-idaho"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "quote": {
                      "type": "object",
                      "properties": {
                        "slug": {
                          "type": "string",
                          "example": "austin-texas"
                        },
                        "city": {
                          "type": "string",
                          "example": "Austin"
                        },
                        "state": {
                          "type": "string",
                          "example": "Texas"
                        },
                        "available": {
                          "type": "boolean"
                        },
                        "subscribers": {
                          "type": "integer"
                        },
                        "tier": {
                          "type": "integer",
                          "example": 1
                        },
                        "priceCents": {
                          "type": "integer",
                          "example": 1000
                        },
                        "subscribersToNextTier": {
                          "type": "integer"
                        },
                        "nextTierPriceCents": {
                          "type": "integer",
                          "example": 1500
                        }
                      }
                    },
                    "quotes": {
                      "type": "array",
                      "description": "Batch mode (`slugs`): one quote per requested slug, keyed by slug.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string",
                            "example": "austin-texas"
                          },
                          "available": {
                            "type": "boolean"
                          },
                          "subscribers": {
                            "type": "integer"
                          },
                          "tier": {
                            "type": "integer",
                            "example": 1
                          },
                          "priceCents": {
                            "type": "integer",
                            "example": 1000
                          },
                          "subscribersToNextTier": {
                            "type": "integer"
                          },
                          "nextTierPriceCents": {
                            "type": "integer",
                            "example": 1500
                          }
                        }
                      }
                    },
                    "claimedCount": {
                      "type": "integer"
                    },
                    "claimed": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slug": {
                            "type": "string"
                          },
                          "city": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/cities": {
      "get": {
        "tags": [
          "cities"
        ],
        "summary": "List active cities grouped by state",
        "description": "Returns every city that currently has at least one subscriber. Cached 1h at the edge.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CitiesResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/cities-census": {
      "get": {
        "tags": [
          "cities"
        ],
        "summary": "List census-tracked cities (filterable by population/state)",
        "parameters": [
          {
            "name": "minPopulation",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1000,
              "default": 5000
            },
            "description": "Lower bound on city population."
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Optional state name filter (e.g. \"Texas\")."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CensusCitiesResponse"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/city/{slug}": {
      "get": {
        "tags": [
          "cities"
        ],
        "summary": "City details, archive list, and census snapshot",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "City slug in `{city}-{state-name}` form, e.g. `austin-texas`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CityResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/facts/{slug}": {
      "get": {
        "tags": [
          "facts"
        ],
        "summary": "Schema.org Dataset facts for a city (AI-friendly)",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "City slug in `{city}-{state-name}` form, e.g. `austin-texas`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/weather/{slug}": {
      "get": {
        "tags": [
          "weather"
        ],
        "summary": "Today's weather for a city",
        "description": "Returns the snapshot stored alongside today's newsletter when available, otherwise a live Open-Meteo fetch.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "City slug in `{city}-{state-name}` form, e.g. `austin-texas`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WeatherResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/archive/{slug}/{date}": {
      "get": {
        "tags": [
          "archives"
        ],
        "summary": "A single past newsletter edition (HTML)",
        "description": "Returns the rendered HTML of a past edition. Append `.md` to the date (e.g. `/api/archive/austin-texas/2026-03-22.md`) for a markdown variant.",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "City slug in `{city}-{state-name}` form, e.g. `austin-texas`."
          },
          {
            "name": "date",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}(\\.md)?$"
            },
            "description": "YYYY-MM-DD, optionally suffixed with `.md`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArchiveResponse"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/search": {
      "get": {
        "tags": [
          "search"
        ],
        "summary": "Full-text search across newsletter archives",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 200
            },
            "description": "Search query (FTS5 syntax supported)."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          },
          {
            "name": "slug",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Restrict to a single city."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/subscribe": {
      "post": {
        "tags": [
          "subscribe"
        ],
        "summary": "Subscribe an email to a city's daily newsletter",
        "description": "Writes the subscriber, sends a welcome email, and queues the first daily digest. Disposable email providers and suppressed addresses are silently rejected.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscribeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscribeResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/oembed": {
      "get": {
        "tags": [
          "discovery"
        ],
        "summary": "oEmbed provider for city and edition pages",
        "description": "Returns an oEmbed `rich` response with an iframe embed for a MailerDot city page or dated edition page. Discovery links are injected into those pages' `<head>`.",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            },
            "description": "The MailerDot page URL to embed.",
            "example": "https://mailerdot.com/austin-texas"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "json"
              ],
              "default": "json"
            },
            "description": "Only `json` is supported; `xml` answers 501."
          },
          {
            "name": "maxwidth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Accepted and ignored — the embed is a fixed size."
          },
          {
            "name": "maxheight",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "Accepted and ignored — the embed is a fixed size."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OEmbedResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/feed.xml": {
      "get": {
        "tags": [
          "discovery"
        ],
        "summary": "RSS 2.0 feed of recent newsletter editions across all cities",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/rss+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/.well-known/api-catalog": {
      "get": {
        "tags": [
          "discovery"
        ],
        "summary": "RFC 9727 API catalog (linkset+json)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/linkset+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/developers": {
      "get": {
        "tags": [
          "discovery"
        ],
        "summary": "MailerDot developer portal — quickstart, endpoint reference, error codes, MCP setup",
        "description": "Send `Accept: text/markdown` (or request `/developers.md`) for the agent-readable version.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "tags": [
          "discovery"
        ],
        "summary": "Model Context Protocol endpoint (JSON-RPC 2.0 over streamable HTTP)",
        "description": "Read-only MCP server. Supports `initialize`, `tools/list`, `tools/call`, `resources/list`, `resources/templates/list`, `resources/read`, `prompts/list`, `prompts/get`, and `ping`. A GET returns a human-readable setup page instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "const": "2.0"
                  },
                  "id": {
                    "type": [
                      "string",
                      "integer",
                      "null"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "example": "tools/list"
                  },
                  "params": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Accepted — the request was a notification, so there is no body."
          },
          "default": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Validation error — see `issues[]` for the per-field failures",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "Problem": {
        "description": "Any other failure — wrong method (405, with an `Allow` header), rate limit (429), server error (500), upstream failure (502), or misconfiguration (503). Always problem+json.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "headers": {
          "Allow": {
            "schema": {
              "type": "string"
            },
            "description": "On a 405: the comma-separated list of methods this path supports."
          }
        }
      }
    },
    "schemas": {
      "ProblemCode": {
        "type": "string",
        "description": "Stable, machine-readable error identifier. Branch on this rather than on `status` or on message text.",
        "enum": [
          "city_not_found",
          "conflict",
          "edition_not_found",
          "endpoint_not_found",
          "forbidden",
          "internal_error",
          "invalid_json",
          "invalid_slug",
          "method_not_allowed",
          "missing_slug",
          "not_found",
          "payload_too_large",
          "rate_limited",
          "service_misconfigured",
          "unauthorized",
          "upstream_error",
          "validation_error",
          "weather_unavailable"
        ]
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details, served as `application/problem+json`. Extension members: `code`, `resolution`, `documentation`, `issues`, and `error` (a duplicate of `detail` kept for older clients).",
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "resolution",
          "documentation",
          "error"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "Dereferences to this code's entry on the developer portal."
          },
          "title": {
            "type": "string",
            "description": "Short summary, identical for every instance of the code."
          },
          "status": {
            "type": "integer",
            "description": "Matches the HTTP status."
          },
          "detail": {
            "type": "string",
            "description": "What went wrong with this specific request."
          },
          "instance": {
            "type": "string",
            "description": "The request path that failed."
          },
          "code": {
            "$ref": "#/components/schemas/ProblemCode"
          },
          "resolution": {
            "type": "string",
            "description": "What the caller can do about it."
          },
          "documentation": {
            "type": "string",
            "format": "uri"
          },
          "error": {
            "type": "string",
            "description": "Duplicate of `detail`. Retained for backwards compatibility."
          },
          "issues": {
            "type": "array",
            "description": "Field-level failures. Present on validation errors only.",
            "items": {
              "type": "object",
              "required": [
                "path",
                "message"
              ],
              "properties": {
                "path": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "CityRef": {
        "type": "object",
        "required": [
          "city",
          "slug"
        ],
        "properties": {
          "city": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          }
        }
      },
      "StateGroup": {
        "type": "object",
        "required": [
          "state",
          "cities"
        ],
        "properties": {
          "state": {
            "type": "string"
          },
          "cities": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CityRef"
            }
          }
        }
      },
      "CitiesResponse": {
        "type": "object",
        "required": [
          "states",
          "totalCities"
        ],
        "properties": {
          "states": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StateGroup"
            }
          },
          "totalCities": {
            "type": "integer"
          }
        }
      },
      "CensusCity": {
        "type": "object",
        "required": [
          "city",
          "slug",
          "population",
          "hasSubscribers"
        ],
        "properties": {
          "city": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "population": {
            "type": "integer"
          },
          "hasSubscribers": {
            "type": "boolean"
          }
        }
      },
      "CensusStateGroup": {
        "type": "object",
        "required": [
          "state",
          "cityCount",
          "totalPopulation",
          "cities"
        ],
        "properties": {
          "state": {
            "type": "string"
          },
          "cityCount": {
            "type": "integer"
          },
          "totalPopulation": {
            "type": "integer"
          },
          "cities": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CensusCity"
            }
          }
        }
      },
      "CensusCitiesResponse": {
        "type": "object",
        "required": [
          "states",
          "totalCities",
          "minPopulation"
        ],
        "properties": {
          "states": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CensusStateGroup"
            }
          },
          "totalCities": {
            "type": "integer"
          },
          "minPopulation": {
            "type": "integer"
          }
        }
      },
      "CityResponse": {
        "type": "object",
        "required": [
          "slug",
          "city",
          "state",
          "subscriberCount",
          "archives"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "subscriberCount": {
            "type": "integer"
          },
          "archives": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "date",
                "subject"
              ],
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "subject": {
                  "type": "string"
                }
              }
            }
          },
          "census": {
            "$ref": "#/components/schemas/Census"
          }
        }
      },
      "Census": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "population": {
            "type": [
              "integer",
              "null"
            ]
          },
          "medianIncome": {
            "type": [
              "integer",
              "null"
            ]
          },
          "medianHomeValue": {
            "type": [
              "integer",
              "null"
            ]
          },
          "medianRent": {
            "type": [
              "integer",
              "null"
            ]
          },
          "ownerOccupied": {
            "type": [
              "integer",
              "null"
            ]
          },
          "renterOccupied": {
            "type": [
              "integer",
              "null"
            ]
          },
          "collegeDegreePercent": {
            "type": [
              "number",
              "null"
            ]
          },
          "avgCommuteMinutes": {
            "type": [
              "number",
              "null"
            ]
          },
          "workFromHomePercent": {
            "type": [
              "number",
              "null"
            ]
          },
          "walkBikeTransitPercent": {
            "type": [
              "number",
              "null"
            ]
          },
          "medianAge": {
            "type": [
              "number",
              "null"
            ]
          },
          "over65Percent": {
            "type": [
              "number",
              "null"
            ]
          },
          "vacancyRate": {
            "type": [
              "number",
              "null"
            ]
          },
          "year": {
            "type": "string"
          }
        }
      },
      "ArchiveResponse": {
        "type": "object",
        "required": [
          "html",
          "subject",
          "date"
        ],
        "properties": {
          "html": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "WeatherResponse": {
        "type": "object",
        "required": [
          "high",
          "low",
          "current",
          "code",
          "condition",
          "emoji"
        ],
        "properties": {
          "high": {
            "type": "number"
          },
          "low": {
            "type": "number"
          },
          "current": {
            "type": "number"
          },
          "code": {
            "type": "integer"
          },
          "condition": {
            "type": "string"
          },
          "emoji": {
            "type": "string"
          },
          "precipitationChance": {
            "type": [
              "number",
              "null"
            ]
          },
          "windSpeed": {
            "type": [
              "number",
              "null"
            ]
          },
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          },
          "timezone": {
            "type": "string"
          },
          "asOf": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "required": [
          "slug",
          "city",
          "state",
          "date",
          "subject",
          "snippet"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "subject": {
            "type": "string"
          },
          "snippet": {
            "type": "string",
            "description": "HTML snippet with `<mark>` highlights around matched terms."
          }
        }
      },
      "SearchResponse": {
        "type": "object",
        "required": [
          "query",
          "results",
          "total"
        ],
        "properties": {
          "query": {
            "type": "string"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchResult"
            }
          },
          "total": {
            "type": "integer"
          }
        }
      },
      "SubscribeRequest": {
        "type": "object",
        "required": [
          "email",
          "city",
          "state",
          "slug"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "honeypot": {
            "type": "string",
            "description": "Anti-bot field. Leave empty."
          },
          "referrer": {
            "type": "string"
          },
          "utm_source": {
            "type": [
              "string",
              "null"
            ]
          },
          "utm_medium": {
            "type": [
              "string",
              "null"
            ]
          },
          "utm_campaign": {
            "type": [
              "string",
              "null"
            ]
          },
          "landing_path": {
            "type": "string"
          }
        }
      },
      "SubscribeResponse": {
        "type": "object",
        "required": [
          "success"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "OEmbedResponse": {
        "type": "object",
        "description": "An oEmbed 1.0 `rich` response. See https://oembed.com/",
        "required": [
          "type",
          "version",
          "html",
          "width",
          "height"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "rich"
          },
          "version": {
            "type": "string",
            "const": "1.0"
          },
          "provider_name": {
            "type": "string",
            "example": "MailerDot"
          },
          "provider_url": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "html": {
            "type": "string",
            "description": "An `<iframe>` element ready to insert."
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "MailerDot developer portal",
    "url": "https://mailerdot.com/developers"
  }
}