{
  "openapi": "3.1.0",
  "info": {
    "title": "Private family browser bus times",
    "version": "2026-10-31",
    "license": { "name": "Proprietary" },
    "description": "Private browser contract, not a supported developer or MCP API. A published Harris family sharing link is the capability. Never disclose working link tokens. Responses are private, no-store, noindex/nofollow and no-referrer. The stop, line and provider URL are fixed server-side."
  },
  "servers": [{ "url": "https://bombyxlabs.com" }],
  "security": [],
  "paths": {
    "/app/family/{token}/bus-arrivals": {
      "get": {
        "operationId": "privateFamilyBusTimes",
        "summary": "Read the best available L1 times for stop 15151",
        "description": "Uses the existing published-family lookup and Harris organization restriction. All shared-link lookups share 120/minute and 1000/hour per-IP limits. Failed short-link guesses also share 1000/hour across IPs. A successful empty observation clears older predictions. Failed checks retain useful predictions from the latest successful observation for strictly less than five minutes, then use the official timetable. Provider retries honor the bounded Retry-After deadline. No query parameter can select another stop or upstream URL.",
        "parameters": [{ "name": "token", "in": "path", "required": true, "description": "Existing private family sharing token. Legacy links use 22 characters; the Harris short route uses eight lowercase letters.", "schema": { "type": "string", "minLength": 8, "maxLength": 22 } }],
        "responses": {
          "200": {
            "description": "Useful live, recent or scheduled times. A provider outage still returns HTTP 200 with status unavailable and a useful display mode.",
            "headers": { "Retry-After": { "description": "Seconds until another provider check is allowed; present when status is unavailable.", "schema": { "type": "integer", "minimum": 1, "maximum": 86400 } } },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BusTimes" } } }
          },
          "404": { "description": "Unknown, unpublished or non-Harris family link." },
          "429": { "description": "Existing family-link or endpoint request budget exceeded. The page's independent timetable remains usable.", "headers": { "Retry-After": { "schema": { "type": "integer", "minimum": 1 } } } }
        }
      }
    },
    "/app/family/harris/{token}/bus-arrivals": { "$ref": "#/paths/~1app~1family~1{token}~1bus-arrivals" }
  },
  "components": {
    "schemas": {
      "BusTimes": {
        "type": "object",
        "additionalProperties": false,
        "required": ["status", "mode", "stop", "arrivals", "fetched_at", "generated_at", "estimate_expires_at", "retry_after"],
        "properties": {
          "status": { "type": "string", "enum": ["available", "empty", "unavailable"], "description": "Latest provider result, separate from the displayed source." },
          "mode": { "type": "string", "enum": ["live", "recent", "scheduled"], "description": "Live: current successful observation under 90 seconds old. Recent: useful retained predictions under five minutes old. Scheduled: official timetable, never a live prediction." },
          "stop": { "type": "object", "additionalProperties": false, "required": ["id", "name", "line"], "properties": { "id": { "const": "15151" }, "name": { "const": "Escola Montjuïc" }, "line": { "const": "L1" } } },
          "arrivals": { "type": "array", "minItems": 1, "maxItems": 3, "items": { "type": "object", "additionalProperties": false, "required": ["minutes", "destination", "expected_at"], "properties": {
            "minutes": { "type": "integer", "minimum": 0, "maximum": 1440, "description": "Remaining rounded-up minutes at generated_at; recalculated from the absolute expected time. Zero means Due, with at most a 30-second grace for rounded source times and request latency." },
            "destination": { "type": "string", "maxLength": 160, "description": "Plain provider text for predictions; may explicitly be blank. Scheduled times use the reviewed timetable direction." },
            "expected_at": { "type": "string", "format": "date-time", "description": "Absolute prediction or scheduled departure. Predictions are derived once from fetched_at plus provider minutes." },
            "calendar_verified": { "type": "boolean", "description": "Scheduled rows only. True when the departure's Girona public-holiday year has been reviewed. False requires a visible typical-timetable qualification." }
          } } },
          "fetched_at": { "type": ["string", "null"], "format": "date-time", "description": "Original latest-successful retrieval time when retained, including an empty observation; never a verified vehicle update timestamp." },
          "generated_at": { "type": "string", "format": "date-time" },
          "estimate_expires_at": { "type": ["string", "null"], "format": "date-time", "description": "Absolute hard expiry, fetched_at plus 300 seconds. Null in scheduled mode. Cache hits and retries never extend it." },
          "retry_after": { "type": "integer", "minimum": 0, "maximum": 86400, "description": "Zero for successful provider observations; remaining provider cooldown seconds for failures." }
        }
      }
    }
  }
}
