{
 "openapi": "3.1.0",
 "info": {
  "title": "SFomo data API",
  "version": "0.2.0",
  "summary": "Read-only Bay Area events.",
  "description": "Public, read-only, no authentication. Data is CC BY 4.0: credit SFomo (https://sfomo.ai) and link each event to its original listing. Agents may use it; see https://sfomo.ai/terms/. An MCP server (Streamable HTTP, JSON-RPC) with tools search_events, list_upcoming and get_event is at https://sfomo.ai/mcp (setup: https://sfomo.ai/agents/). Coverage and accuracy: https://sfomo.ai/coverage/. Changes: https://sfomo.ai/changelog/.",
  "license": {
   "name": "CC BY 4.0",
   "url": "https://creativecommons.org/licenses/by/4.0/"
  },
  "contact": {
   "email": "admin@whatsupin.ai"
  }
 },
 "servers": [
  {
   "url": "https://sfomo.ai"
  }
 ],
 "paths": {
  "/data/events.json": {
   "get": {
    "operationId": "getEvents",
    "summary": "Upcoming events, nearest weeks",
    "responses": {
     "200": {
      "description": "Events plus `rest`, the path of the later-events file.",
      "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 file holding later events (data/events_rest.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",
    "responses": {
     "200": {
      "description": "Same event shape.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "events": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Event"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/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"
      ]
     },
     "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."
     },
     "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."
     },
     "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."
     }
    }
   }
  }
 }
}
