{
 "openapi": "3.1.0",
 "info": {
  "title": "Colorado Springs & Pueblo Events data API",
  "version": "0.16.0",
  "summary": "Read-only Colorado Springs, Pueblo & Southern Colorado events.",
  "description": "Public, read-only, no authentication. All rights reserved: an assistant may answer one person's question from it, crediting Colorado Springs & Pueblo Events (https://coloradosprings.whatsupin.ai) and linking each event to its original listing; bulk copying needs express written permission (admin@whatsupin.ai). Terms: https://coloradosprings.whatsupin.ai/terms/#data. An MCP server (Streamable HTTP, JSON-RPC) with tools search_events, list_upcoming, get_event, get_city and find_performer is at https://coloradosprings.whatsupin.ai/mcp (setup: https://coloradosprings.whatsupin.ai/agents/). Coverage and accuracy: https://coloradosprings.whatsupin.ai/coverage/. Changes: https://coloradosprings.whatsupin.ai/changelog/.",
  "license": {
   "name": "All rights reserved; no bulk copying without written permission",
   "url": "https://coloradosprings.whatsupin.ai/terms/#data"
  },
  "contact": {
   "email": "admin@whatsupin.ai"
  }
 },
 "servers": [
  {
   "url": "https://coloradosprings.whatsupin.ai"
  }
 ],
 "paths": {
  "/data/events.json": {
   "get": {
    "operationId": "getEvents",
    "summary": "Upcoming events, nearest weeks",
    "responses": {
     "200": {
      "description": "Events plus `rest_parts`, the paths of the later-events files.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "events": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Event"
           }
          },
          "rest": {
           "type": "string",
           "description": "Path of the first file holding later events (data/events_rest.json)."
          },
          "rest_parts": {
           "type": "array",
           "items": {
            "type": "string"
           },
           "description": "Paths of every later-events file, in order (data/events_rest.json, data/events_rest_2.json, ...)."
          },
          "rest_count": {
           "type": "integer"
          },
          "generated": {
           "type": "string",
           "format": "date-time",
           "description": "When this data was published."
          },
          "license": {
           "type": "object"
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/data/events_rest.json": {
   "get": {
    "operationId": "getLaterEvents",
    "summary": "Events further out (the first part; events.json `rest_parts` lists any more)",
    "responses": {
     "200": {
      "description": "Same event shape.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "events": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Event"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/data/changes.json": {
   "get": {
    "operationId": "getChanges",
    "summary": "Upcoming events taken down in the last two weeks (cancelled, delisted or merged into another card), and when change tracking began",
    "responses": {
     "200": {
      "description": "`removed`: [{id, title, date, removed}], newest first; `tracked_since`; `generated`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/data/coverage.json": {
   "get": {
    "operationId": "getCoverage",
    "summary": "Counts, per-source freshness, latest validator numbers",
    "responses": {
     "200": {
      "description": "Coverage.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/api/status": {
   "get": {
    "operationId": "getStatus",
    "summary": "Is the service up, and how old is the data",
    "responses": {
     "200": {
      "description": "status ok, or degraded when the data is older than 48 hours.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/llms.txt": {
   "get": {
    "operationId": "getLlmsTxt",
    "summary": "Plain-text pointer for LLM clients",
    "responses": {
     "200": {
      "description": "text/plain"
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Event": {
    "type": "object",
    "description": "One upcoming event in data/events.json (and data/events_rest.json). Fields marked optional are absent when unknown.",
    "required": [
     "id",
     "title",
     "date",
     "link"
    ],
    "properties": {
     "id": {
      "type": "string",
      "pattern": "^[0-9a-f]{12}$",
      "description": "Stable public id. Kept while the event stays listed: a retitle, reschedule or new link by the source does not change it. Ids are never reused for a different event."
     },
     "title": {
      "type": "string"
     },
     "date": {
      "type": "string",
      "format": "date",
      "description": "Local start date (site time zone)."
     },
     "start_time": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time",
      "description": "Local start with UTC offset; null for all-day or untimed."
     },
     "end_time": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     },
     "location": {
      "type": [
       "string",
       "null"
      ]
     },
     "hood": {
      "type": "string",
      "description": "Optional. Home-city neighborhood of the venue (MCP calls this `neighborhood`)."
     },
     "ll": {
      "type": "array",
      "items": {
       "type": "number"
      },
      "minItems": 2,
      "maxItems": 2,
      "description": "[lat, lon] of the venue (optional)."
     },
     "price": {
      "type": [
       "string",
       "null"
      ],
      "description": "Only an all-in price or \"Free\". When the source does not state the all-in total there is no number; see tix."
     },
     "free": {
      "type": "boolean",
      "description": "Optional. True when the listing is free."
     },
     "tix": {
      "type": "boolean",
      "description": "Optional. Paid event; the source does not state an all-in price, so none is shown."
     },
     "categories": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "tags": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "topic/<name> tags and others."
     },
     "performers": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Optional."
     },
     "sold_out": {
      "type": "boolean",
      "description": "Optional. True when every posting we know of says tickets are gone (a status, not a price)."
     },
     "event_status": {
      "type": "string",
      "enum": [
       "sold_out",
       "postponed",
       "rescheduled",
       "moved"
      ],
      "description": "Optional. What a source says about the event: postponed (no new date yet; it stays on its original date), rescheduled (this is the new date), moved (to another venue; location is the new one), sold_out. Absent = scheduled as listed. Cancelled events are left out."
     },
     "status_note": {
      "type": "string",
      "description": "Optional. The source's own wording of event_status, e.g. \"Moved to The Warfield\" or \"Rescheduled from 8/8/26\"."
     },
     "vt": {
      "type": "string",
      "format": "date-time",
      "description": "Optional. Checked live: when we last opened the original listing and found it matching (start time, venue, not cancelled), in local time. Absent = not checked in the last two days."
     },
     "recurring": {
      "type": "string",
      "enum": [
       "weekly",
       "biweekly",
       "monthly"
      ],
      "description": "Optional. The event is one date of a repeating series (same title at the same venue)."
     },
     "link": {
      "type": [
       "string",
       "null"
      ],
      "format": "uri",
      "description": "The original listing. Link to it when you use the event."
     },
     "srcs": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Every source that lists the event (MCP calls this `sources`)."
     },
     "org": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Organizer per source, aligned with srcs (optional)."
     },
     "fs": {
      "type": "string",
      "format": "date",
      "description": "first_seen: first day a fetch of the source listed this event."
     },
     "ls": {
      "type": "string",
      "format": "date",
      "description": "last_seen: latest day a fetch of the source listed it. Older than ft means the source has stopped listing it."
     },
     "ft": {
      "type": "string",
      "format": "date",
      "description": "last_fetched: the day the source was last fetched."
     },
     "ch": {
      "type": "string",
      "description": "changed: when an export last saw the title, date or time, venue, link, price, sold-out flag or status change (local ISO date-time to the minute). Events listed before change tracking began carry their first-seen day. The MCP's changed_since filters on it."
     },
     "dup_count": {
      "type": "integer",
      "description": "Optional. Number of sources merged into this card."
     },
     "rv": {
      "type": "string",
      "description": "Provenance fingerprint (keyed by us). Keep it if you copy the record."
     }
    }
   }
  }
 }
}
