{
  "openapi": "3.1.0",
  "info": {
    "title": "WenToGo API",
    "version": "1.0.0",
    "description": "Read-only timing scores for exact trip dates across 180+ researched destinations. No authentication; no state changes. Fair use is enforced at the edge across every /api/ path, this endpoint included: 20 requests per 10 seconds per client IP, then 429. The prerendered HTML pages carry no limit. Keep request rates modest and prefer the prerendered HTML pages documented at https://wentogo.com/llms.txt for browsing. Every origin-generated payload carries source_version: identical request inputs under the same version return identical answers, so cache responses and refresh them only when the version changes. Every origin-generated error from the documented API is JSON in the structured shape {source_version, error:{code, message, details?}}: an unknown or withdrawn API path answers 404 with the NOT_PART_OF_PUBLIC_API code, and a non-GET request to this endpoint answers 405 with the METHOD_NOT_ALLOWED code and an Allow header when it carries a matching Origin, or a plain 403 from the framework's origin check when it does not. The edge-owned 429 response may use a different body and content type."
  },
  "servers": [{ "url": "https://wentogo.com" }],
  "paths": {
    "/api/trip/{country}/{destination}/{start}/{end}": {
      "get": {
        "operationId": "getTripTiming",
        "summary": "Score one dated trip",
        "description": "How good those exact dates are for the destination, what drags the score down and which nearby windows beat it. The HTML equivalent lives at /trip/{country}/{destination}/{start}/{end}.",
        "parameters": [
          {
            "name": "country",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^[a-z0-9-]+$" },
            "example": "japan"
          },
          {
            "name": "destination",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^[a-z0-9-]+$" },
            "example": "tokyo"
          },
          {
            "name": "start",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "example": "2027-04-01"
          },
          {
            "name": "end",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "example": "2027-04-08"
          }
        ],
        "responses": {
          "200": {
            "description": "Scored trip payload",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/TripApiResponse" }
              }
            }
          },
          "400": {
            "description": "Malformed dates or parameters",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiErrorResponse" }
              }
            }
          },
          "404": {
            "description": "Unknown country or destination",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiErrorResponse" }
              }
            }
          },
          "405": {
            "description": "Method not allowed. This endpoint accepts GET only; the response carries an Allow header. A cross-site non-GET request is refused earlier, with a plain 403, by the framework origin check.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiErrorResponse" }
              }
            }
          },
          "429": {
            "description": "Rate limited by the Cloudflare edge before the request reaches the application. The response body and content type are edge-owned and clients must not depend on the origin error schema."
          },
          "default": {
            "description": "Unexpected origin failure",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiErrorResponse" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "TripApiResponse": {
        "type": "object",
        "required": ["source_version", "request", "page_payload"],
        "properties": {
          "source_version": { "type": "string" },
          "request": {
            "type": "object",
            "properties": {
              "country_slug": { "type": "string" },
              "destination_slug": { "type": "string" },
              "start_date": { "type": "string", "format": "date" },
              "end_date": { "type": "string", "format": "date" },
              "preferences": { "$ref": "#/components/schemas/ApiPreferences" }
            }
          },
          "page_payload": {
            "type": "object",
            "additionalProperties": true,
            "properties": {
              "destination": {
                "type": "object",
                "properties": {
                  "country": { "type": "string" },
                  "name": { "type": "string" },
                  "slug": { "type": "string" }
                }
              },
              "trip": {
                "type": "object",
                "properties": {
                  "start_date": { "type": "string", "format": "date" },
                  "end_date": { "type": "string", "format": "date" },
                  "length_days": { "type": "integer" },
                  "score": { "type": "number" },
                  "grade": { "type": "string" },
                  "verdict": { "type": "string" }
                }
              },
              "hero": {
                "type": "object",
                "properties": {
                  "title": { "type": "string" },
                  "summary": { "type": "string" },
                  "strongest_factor": { "type": "string" },
                  "weakest_factor": { "type": "string" }
                }
              },
              "factor_cards": {
                "type": "array",
                "items": { "type": "object", "additionalProperties": true }
              },
              "why_go": {
                "type": "array",
                "items": { "type": "object", "additionalProperties": true }
              },
              "watch_out": {
                "type": "array",
                "items": { "type": "object", "additionalProperties": true }
              }
            }
          }
        }
      },
      "ApiPreferences": {
        "type": "object",
        "properties": {
          "state": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "enum": ["low", "neutral", "high"]
            }
          },
          "changed": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "enum": ["low", "neutral", "high"]
            }
          },
          "importance": {
            "type": "object",
            "additionalProperties": { "type": "number" }
          },
          "query_string": { "type": "string" }
        }
      },
      "ApiErrorResponse": {
        "type": "object",
        "required": ["source_version", "error"],
        "properties": {
          "source_version": { "type": "string" },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "INVALID_QUERY",
                  "QUERY_TOO_SHORT",
                  "QUERY_TOO_LONG",
                  "INVALID_LIMIT",
                  "INVALID_LENGTH",
                  "INVALID_RADIUS_DAYS",
                  "MISSING_START_DATE",
                  "MISSING_END_DATE",
                  "MISSING_LENGTH",
                  "LENGTH_MISMATCH",
                  "INVALID_COUNTRY",
                  "INVALID_PREFERENCE",
                  "DUPLICATE_PREFERENCE_KEY",
                  "MISSING_ROUTE_PARAM",
                  "NOT_PART_OF_PUBLIC_API",
                  "METHOD_NOT_ALLOWED",
                  "INVALID_DATE",
                  "END_BEFORE_START",
                  "RANGE_TOO_LONG",
                  "UNSUPPORTED_YEAR",
                  "UNKNOWN_DESTINATION",
                  "UNKNOWN_EVENT",
                  "UNKNOWN_WEIGHT_KEY",
                  "DUPLICATE_WEIGHT_KEY",
                  "INVALID_WEIGHT",
                  "NEGATIVE_WEIGHT",
                  "INVALID_WEIGHT_TOTAL",
                  "DUPLICATE_IMPORTANCE_KEY",
                  "INVALID_IMPORTANCE",
                  "NEGATIVE_IMPORTANCE",
                  "MISSING_REASON_TEMPLATE",
                  "INTERNAL_ERROR"
                ]
              },
              "message": { "type": "string" },
              "details": { "type": "object", "additionalProperties": true }
            }
          }
        }
      }
    }
  }
}
