{
  "openapi": "3.1.0",
  "info": {
    "title": "Xổ Số Nay - API kết quả xổ số Việt Nam",
    "description": "Kết quả xổ số 3 miền (XSMB/XSMN/XSMT) và Vietlott từ các dòng đã xác minh trong cơ sở dữ liệu. Mỗi phản hồi nêu cấp bằng chứng nguồn; thiếu nhật ký nguồn theo kỳ sẽ được ghi rõ. Miễn phí, CC BY 4.0, không cần khóa API. Kết quả chỉ mang tính tham khảo.",
    "version": "1.4.0",
    "license": { "name": "CC BY 4.0", "url": "https://creativecommons.org/licenses/by/4.0/" }
  },
  "servers": [{ "url": "https://xosonay.com" }],
  "security": [],
  "paths": {
    "/api/{mien}/latest.json": {
      "get": {
        "operationId": "getLatestResults",
        "summary": "Kết quả kỳ quay mới nhất hiện có của một miền",
        "parameters": [{ "name": "mien", "in": "path", "required": true,
          "schema": { "type": "string", "enum": ["xsmb", "xsmn", "xsmt"] } }],
        "responses": { "200": { "description": "Kết quả đầy đủ các giải theo từng đài",
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LatestDayResult" } } } },
          "404": { "description": "Miền không hợp lệ hoặc chưa có dữ liệu" } }
      }
    },
    "/api/{mien}/{date}.json": {
      "get": {
        "operationId": "getResultsByDate",
        "summary": "Kết quả một ngày cụ thể (DD-MM-YYYY)",
        "parameters": [
          { "name": "mien", "in": "path", "required": true,
            "schema": { "type": "string", "enum": ["xsmb", "xsmn", "xsmt"] } },
          { "name": "date", "in": "path", "required": true,
            "schema": { "type": "string", "pattern": "^\\d{2}-\\d{2}-\\d{4}$" },
            "example": "08-07-2026" }
        ],
        "responses": { "200": { "description": "Kết quả ngày đó (404 nếu chưa có dữ liệu)",
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DayResult" } } } },
          "404": { "description": "Chưa có dữ liệu" } }
      }
    },
    "/data/{mien}.json": {
      "get": {
        "operationId": "downloadHistoricalResultsJson",
        "summary": "Tải toàn bộ dữ liệu hiện có của một miền dưới dạng JSON",
        "description": "Một tệp chứa toàn bộ bản ghi verified=1 hiện có trong cơ sở dữ liệu xosonay.com cho miền đã chọn; evidenceLevel cho biết tệp không kèm nhật ký nguồn chi tiết theo từng kỳ và không cam kết bao quát toàn bộ lịch sử xổ số. Metadata tự mô tả phân biệt generatedAt (lúc xuất tệp) với dateModified (MAX fetched_at của các bản ghi DB).",
        "parameters": [{ "name": "mien", "in": "path", "required": true,
          "schema": { "type": "string", "enum": ["xsmb", "xsmn", "xsmt"] } }],
        "responses": { "200": { "description": "Tập dữ liệu hiện có cùng metadata, tên trường và các hàng dữ liệu",
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HistoricalDataset" } } } },
          "404": { "description": "Miền không hợp lệ hoặc tập dữ liệu chưa tồn tại" } }
      }
    },
    "/data/{mien}.csv": {
      "get": {
        "operationId": "downloadHistoricalResultsCsv",
        "summary": "Tải toàn bộ dữ liệu hiện có của một miền dưới dạng CSV",
        "description": "CSV UTF-8 giữ nguyên hàng tiêu đề date, province, prize, numbers và không nhúng metadata để tương thích với trình đọc CSV. Xem tệp JSON cùng miền tại /data/{mien}.json để lấy schemaVersion, phạm vi thời gian, thời điểm sửa dữ liệu, phương pháp, trích dẫn và nguồn gốc. Các giá trị numbers được giữ dưới dạng chuỗi để không mất số 0 ở đầu. Phạm vi là toàn bộ dữ liệu hiện có, không phải cam kết lịch sử đầy đủ.",
        "parameters": [{ "name": "mien", "in": "path", "required": true,
          "schema": { "type": "string", "enum": ["xsmb", "xsmn", "xsmt"] } }],
        "responses": { "200": { "description": "Tập dữ liệu hiện có dạng CSV",
          "content": { "text/csv": { "schema": { "type": "string" } } } },
          "404": { "description": "Miền không hợp lệ hoặc tập dữ liệu chưa tồn tại" } }
      }
    },
    "/api/vietlott/{product}.json": {
      "get": {
        "operationId": "getVietlottLatest",
        "summary": "Vietlott kỳ mới nhất hiện có",
        "parameters": [{ "name": "product", "in": "path", "required": true,
          "schema": { "type": "string", "enum": ["mega-6-45", "power-6-55", "lotto-5-35", "max-3d", "max-3d-pro"] } }],
        "responses": { "200": { "description": "Kết quả Vietlott đã xác minh trong cơ sở dữ liệu, kèm cấp bằng chứng nguồn",
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VietlottResult" },
            "example": { "product": "lotto-5-35", "name": "Lotto 5/35",
              "date": "2026-07-20", "drawTime": "13:00", "ky": "00773",
              "numbers": ["03", "08", "23", "27", "35"], "specialBall": "08",
              "jackpot": "6751232500", "updatedAt": "2026-07-20T13:58:37+07:00",
              "generatedAt": "2026-07-20T14:00:00+07:00", "asOf": "2026-07-20",
              "asOfDrawTime": "13:00", "verifiedAt": "2026-07-20T13:58:37+07:00",
              "lagDays": 0, "freshnessStatus": "fresh", "expectedAsOf": "2026-07-20",
              "expectedDrawTime": "13:00", "freshnessMessage": "Dữ liệu đã xác minh đến kỳ 2026-07-20 13:00.",
              "provenance": { "version": "1.0", "sourcePage": "https://xosonay.com/vietlott/lotto-5-35/",
                "officialPublisher": ["https://info.vietlottsms.vn/lotto535/"],
                "officialPublisherScope": "product-official-publisher", "acquiredFrom": [],
                "methodologyUrl": "https://xosonay.com/phuong-phap/",
                "verificationRecordUrl": "https://xosonay.com/api/vietlott/lotto-5-35.json",
                "verifiedAt": "2026-07-20T13:58:37+07:00",
                "license": "https://creativecommons.org/licenses/by/4.0/",
                "evidenceLevel": "db-verified-no-source-log" },
              "disclaimer": "Chỉ mang tính tham khảo; kết quả chính thức theo công bố của Vietlott." } } } },
          "404": { "description": "Sản phẩm không hợp lệ hoặc chưa có dữ liệu" } }
      }
    },
    "/api/dreams-v2.json": {
      "get": {
        "operationId": "getDreamIndexV2",
        "summary": "Tra chỉ mục sổ mơ dạng envelope có nguồn gốc chung",
        "description": "Endpoint ưu tiên cho tích hợp mới. Trường dreams chứa các hàng compact theo đúng thứ tự [searchKeyword, displayName, numbers, slug]. Metadata nguồn gốc áp dụng chung cho toàn bộ bảng: đây là bản tổng hợp quan niệm dân gian, hiện không có trích dẫn nguồn theo từng mục, không thể hiện sự đồng thuận giữa các bảng sổ mơ và không phải dự đoán.",
        "responses": {
          "200": { "description": "Envelope schemaVersion=2, metadata nguồn gốc trung thực và các hàng sổ mơ compact",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DreamIndexV2" } } } },
          "404": { "description": "Không tìm thấy endpoint trong bản phát hành đang phục vụ" }
        }
      }
    },
    "/api/dreams.json": {
      "get": {
        "operationId": "getLegacyDreamIndex",
        "summary": "Chỉ mục sổ mơ legacy dạng mảng trần",
        "description": "Hợp đồng legacy tương thích cho client cũ: phản hồi là mảng trần chứa cùng các hàng compact [searchKeyword, displayName, numbers, slug] như trường dreams của v2, nhưng không có envelope nguồn gốc. Tích hợp mới nên dùng /api/dreams-v2.json và đọc caveat cùng provenanceStatus.",
        "deprecated": true,
        "responses": {
          "200": { "description": "Mảng trần legacy gồm các hàng sổ mơ compact",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LegacyDreamIndex" } } } },
          "404": { "description": "Không tìm thấy endpoint trong bản phát hành đang phục vụ" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "DreamCompactRow": {
        "type": "array",
        "description": "Hàng compact theo vị trí: searchKeyword, displayName, numbers, slug.",
        "prefixItems": [
          { "type": "string", "minLength": 1, "description": "Từ khóa dùng để tìm kiếm." },
          { "type": "string", "minLength": 1, "description": "Tên mục hiển thị." },
          { "type": "array", "items": { "type": "string", "pattern": "^\\d{2}$" }, "minItems": 1, "description": "Các bộ số dân gian, giữ dạng chuỗi hai ký tự." },
          { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", "description": "Slug của trang chi tiết." }
        ],
        "items": false,
        "minItems": 4,
        "maxItems": 4
      },
      "DreamIndexV2": {
        "type": "object",
        "additionalProperties": false,
        "required": ["schemaVersion", "folkClaim", "provenanceStatus", "provenanceLabel", "caveat", "methodologyUrl", "pageUrlTemplate", "entryCount", "dreams"],
        "properties": {
          "schemaVersion": { "type": "integer", "const": 2 },
          "folkClaim": { "type": "boolean", "const": true, "description": "Các bộ số là quan niệm dân gian, không phải dự đoán." },
          "provenanceStatus": { "type": "string", "const": "entry-level-citations-unavailable" },
          "provenanceLabel": { "type": "string", "minLength": 1 },
          "caveat": { "type": "string", "minLength": 1 },
          "methodologyUrl": { "type": "string", "format": "uri", "const": "https://xosonay.com/phuong-phap/#so-mo-dan-gian" },
          "pageUrlTemplate": { "type": "string", "format": "uri-template", "const": "https://xosonay.com/so-mo/{slug}/" },
          "entryCount": { "type": "integer", "minimum": 0, "description": "Số hàng trong dreams." },
          "dreams": { "type": "array", "items": { "$ref": "#/components/schemas/DreamCompactRow" } }
        }
      },
      "LegacyDreamIndex": {
        "type": "array",
        "description": "Hợp đồng legacy: mảng trần chứa đúng các hàng trong DreamIndexV2.dreams, không có metadata envelope.",
        "items": { "$ref": "#/components/schemas/DreamCompactRow" }
      },
      "DayResult": {
        "type": "object",
        "required": ["region", "regionName", "code", "date", "drawTime", "updatedAt", "results", "provenance", "disclaimer"],
        "properties": {
          "region": { "type": "string", "enum": ["xsmb", "xsmn", "xsmt"] },
          "regionName": { "type": "string" },
          "code": { "type": "string", "enum": ["XSMB", "XSMN", "XSMT"] },
          "date": { "type": "string", "format": "date" },
          "drawTime": { "type": "string", "pattern": "^(16h15|17h15|18h15)$" },
          "updatedAt": { "type": "string", "format": "date-time" },
          "source": { "type": "string" },
          "results": { "type": "array", "items": { "type": "object", "properties": {
            "province": { "type": ["string", "null"] }, "provinceName": { "type": ["string", "null"] },
            "prizes": { "type": "object", "additionalProperties": { "type": "object", "properties": {
              "name": { "type": "string" },
              "numbers": { "type": "array", "items": { "type": "string" } } } } } } } },
          "provenance": { "$ref": "#/components/schemas/Provenance" },
          "disclaimer": { "type": "string" }
        }
      },
      "HistoricalDataset": {
        "type": "object",
        "required": ["region", "license", "source", "schemaVersion", "generatedAt", "dateModified", "temporalCoverage", "drawCount", "rowCount", "coverageStatement", "evidenceLevel", "methodology", "citation", "provenance", "fields", "rows"],
        "properties": {
          "region": { "type": "string", "enum": ["xsmb", "xsmn", "xsmt"] },
          "license": { "type": "string", "const": "CC BY 4.0" },
          "source": { "type": "string" },
          "schemaVersion": { "type": "string", "const": "1.0.0" },
          "generatedAt": { "type": "string", "format": "date-time", "description": "Thời điểm tệp này được tạo; có thể thay đổi khi xuất lại mà dữ liệu không đổi." },
          "dateModified": { "type": "string", "format": "date-time", "description": "MAX fetched_at trong các hàng DB có verified=1 của miền; đây không phải thời gian lấy từng nguồn và không thay đổi do chỉ xuất lại tệp." },
          "temporalCoverage": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}/\\d{4}-\\d{2}-\\d{2}$", "description": "MIN(date)/MAX(date) của các hàng DB có verified=1." },
          "drawCount": { "type": "integer", "minimum": 1, "description": "Số cặp ngày + tỉnh/đài phân biệt trong các hàng verified=1; XSMB có một đài mỗi ngày." },
          "rowCount": { "type": "integer", "minimum": 1, "description": "COUNT(*) hàng giải có verified=1; bằng số phần tử trong rows." },
          "coverageStatement": { "type": "string", "description": "Tuyên bố trung thực về phạm vi dữ liệu hiện có." },
          "evidenceLevel": { "type": "string", "const": "db-verified-no-source-log", "description": "Các hàng thuộc DB verified=1 nhưng tệp không kèm nhật ký nguồn chi tiết theo từng kỳ." },
          "methodology": { "type": "string", "format": "uri", "const": "https://xosonay.com/phuong-phap/" },
          "citation": { "type": "string", "format": "uri", "const": "https://xosonay.com/du-lieu-mo/" },
          "provenance": { "type": "string", "format": "uri", "const": "https://xosonay.com/nhat-ky-doi-chieu/" },
          "fields": { "type": "array", "prefixItems": [
            { "const": "date" }, { "const": "province" }, { "const": "prize" }, { "const": "numbers" }
          ], "items": false, "minItems": 4, "maxItems": 4 },
          "rows": { "type": "array", "items": { "type": "array", "items": { "type": "string" },
            "minItems": 4, "maxItems": 4 }, "minItems": 1 }
        }
      },
      "LatestDayResult": {
        "allOf": [
          { "$ref": "#/components/schemas/DayResult" },
          { "type": "object",
            "required": ["generatedAt", "asOf", "verifiedAt", "lagDays", "freshnessStatus", "expectedAsOf", "freshnessMessage"],
            "properties": {
              "generatedAt": { "type": "string", "format": "date-time" },
              "asOf": { "type": "string", "format": "date" },
              "verifiedAt": { "type": "string", "format": "date-time", "description": "Tên trường tương thích ngược cho fetched_at của bản ghi DB; không phải thời gian lấy từng nguồn." },
              "lagDays": { "type": "integer", "minimum": 0 },
              "freshnessStatus": { "type": "string", "enum": ["fresh", "pre-draw", "stale"] },
              "expectedAsOf": { "type": "string", "format": "date" },
              "freshnessMessage": { "type": "string" }
            }
          }
        ]
      },
      "Provenance": {
        "type": "object",
        "required": ["version", "sourcePage", "officialPublisher", "officialPublisherScope", "acquiredFrom", "methodologyUrl", "verificationRecordUrl", "verifiedAt", "license", "evidenceLevel"],
        "properties": {
          "version": { "type": "string", "const": "1.0" },
          "sourcePage": { "type": "string", "format": "uri" },
          "officialPublisher": { "type": "array", "items": { "type": "string", "format": "uri" }, "minItems": 1 },
          "officialPublisherScope": { "type": "string", "enum": ["regional-official-reference-not-exhaustive", "product-official-publisher"] },
          "acquiredFrom": { "type": "array", "items": { "type": "string", "format": "uri" } },
          "methodologyUrl": { "type": "string", "format": "uri", "const": "https://xosonay.com/phuong-phap/" },
          "verificationIndexUrl": { "type": "string", "format": "uri", "const": "https://xosonay.com/nhat-ky-doi-chieu/" },
          "verificationRecordUrl": { "type": "string", "format": "uri" },
          "verifiedAt": { "type": "string", "format": "date-time", "description": "Tên trường tương thích ngược cho fetched_at của bản ghi DB; không phải thời gian lấy từng nguồn. Thời gian từng nguồn chỉ nằm trong sourceLog.sourceEvidence khi evidenceLevel=source-log." },
          "license": { "type": "string", "format": "uri", "const": "https://creativecommons.org/licenses/by/4.0/" },
          "evidenceLevel": { "type": "string", "enum": ["source-log", "db-verified-no-source-log"] },
          "acquisitionNote": { "type": "string" },
          "sourceLog": { "type": "object", "required": ["matched", "schemaVersion", "fieldScope", "sources", "recordedAt", "sourceEvidence"], "properties": {
            "matched": { "type": "boolean", "const": true },
            "schemaVersion": { "oneOf": [{ "type": "string" }, { "type": "integer", "minimum": 1 }], "description": "Phiên bản hợp đồng bằng chứng của từng dòng nhật ký." },
            "fieldScope": { "oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" }, "minItems": 1 }], "description": "Chính xác các trường đã tham gia so khớp; không mở rộng sang trường ngoài phạm vi này." },
            "sources": { "type": "array", "items": { "type": "string" }, "minItems": 2 },
            "recordedAt": { "type": "string", "format": "date-time", "description": "Thời gian fetchedAt muộn nhất trong sourceEvidence." },
            "sourceEvidence": { "type": "array", "minItems": 2, "items": { "type": "object", "required": ["sourceId", "fetchedAt", "sha256"], "properties": {
              "sourceId": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" },
              "fetchedAt": { "type": "string", "format": "date-time" },
              "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
            } } }
          } }
        }
      },
      "VietlottResult": {
        "type": "object",
        "required": ["product", "name", "date", "updatedAt", "generatedAt", "asOf", "verifiedAt", "lagDays", "freshnessStatus", "expectedAsOf", "expectedDrawTime", "freshnessMessage", "provenance", "disclaimer"],
        "properties": {
          "product": { "type": "string", "enum": ["mega-6-45", "power-6-55", "lotto-5-35", "max-3d", "max-3d-pro"] },
          "name": { "type": "string" },
          "date": { "type": "string", "format": "date" },
          "drawTime": { "type": "string", "pattern": "^(13|21):00$" },
          "ky": { "type": "string" },
          "numbers": { "type": "array", "items": { "type": "string" } },
          "specialBall": { "type": "string", "pattern": "^\\d{2}$" },
          "powerBall": { "type": "string" },
          "jackpot": { "type": "string", "pattern": "^\\d+$" },
          "tiers": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string" } } },
          "updatedAt": { "type": "string", "format": "date-time" },
          "generatedAt": { "type": "string", "format": "date-time" },
          "asOf": { "type": ["string", "null"], "format": "date" },
          "asOfDrawTime": { "type": "string", "pattern": "^(13|18|21):00$" },
          "verifiedAt": { "type": ["string", "null"], "format": "date-time", "description": "Tên trường tương thích ngược cho fetched_at của bản ghi DB; không phải thời gian lấy từng nguồn." },
          "lagDays": { "type": ["integer", "null"], "minimum": 0 },
          "freshnessStatus": { "type": "string", "enum": ["fresh", "stale", "empty"] },
          "expectedAsOf": { "type": "string", "format": "date" },
          "expectedDrawTime": { "type": "string", "pattern": "^(13|18|21):00$" },
          "freshnessMessage": { "type": "string" },
          "provenance": { "$ref": "#/components/schemas/Provenance" },
          "disclaimer": { "type": "string" }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Hướng dẫn API, MCP, ví dụ mã và giấy phép dữ liệu",
    "url": "https://xosonay.com/du-lieu-mo/"
  }
}
