{
  "openapi": "3.1.0",
  "info": {
    "title": "Link by KICK API",
    "version": "1.0.0",
    "summary": "Tạo và quản lý smartlink của nghệ sĩ từ hệ thống của bạn.",
    "description": "Tài liệu đầy đủ: https://link.kick.vn/docs. Mọi request cần header `Authorization: Bearer <token>`; token lấy tại Cài đặt → Tích hợp của nghệ sĩ (gói Pro). Lỗi luôn có dạng `{ \"error\": { \"code\", \"message\" } }`."
  },
  "servers": [{ "url": "https://link.kick.vn/v1" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Smartlink", "description": "Tạo, sửa, xoá, liệt kê smartlink." },
    { "name": "Tiện ích", "description": "Dò bài, tải ảnh bìa, tìm nghệ sĩ để gắn thẻ." },
    { "name": "Tài khoản", "description": "Nghệ sĩ của token và thống kê." }
  ],
  "paths": {
    "/me": {
      "get": {
        "tags": ["Tài khoản"],
        "operationId": "getMe",
        "summary": "Nghệ sĩ của token",
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MeResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/links": {
      "get": {
        "tags": ["Smartlink"],
        "operationId": "listLinks",
        "summary": "Liệt kê smartlink",
        "description": "Mới tạo trước, 20 smartlink mỗi trang.",
        "parameters": [
          { "name": "q", "in": "query", "description": "Tìm theo tên bài hoặc dòng tên nghệ sĩ, không phân biệt hoa thường.", "schema": { "type": "string" } },
          { "name": "page", "in": "query", "description": "Trang, bắt đầu từ 1.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["links", "total", "page_size"],
                  "properties": {
                    "links": { "type": "array", "items": { "$ref": "#/components/schemas/LinkSummary" } },
                    "total": { "type": "integer", "description": "Tổng số smartlink khớp." },
                    "page_size": { "type": "integer", "const": 20 }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["Smartlink"],
        "operationId": "createLink",
        "summary": "Tạo smartlink",
        "description": "Chỉ gửi `isrc` (không có `title`) thì KICK tự dò tên bài, nghệ sĩ, ảnh bìa, ngày phát hành, lời và link các nền tảng (khớp từ 90%), tạo `slug` từ tên bài và đặt `status` là `public`. Trường gửi kèm được giữ nguyên.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LinkInput" },
              "examples": {
                "isrc": { "summary": "Chỉ ISRC", "value": { "isrc": "VNA0A2400001" } },
                "scheduled": {
                  "summary": "Lên lịch",
                  "value": { "title": "Bài Mới", "isrc": "VNA0A2400002", "status": "scheduled", "scheduled_at": "2026-10-20T20:00:00+07:00" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "$ref": "#/components/responses/LinkOk" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "404": { "description": "`isrc_not_found`: chưa có bài với ISRC này trên các nền tảng.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "409": { "description": "`slug_taken`: slug đã có smartlink khác dùng.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/links/{id}": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
      "get": {
        "tags": ["Smartlink"],
        "operationId": "getLink",
        "summary": "Lấy một smartlink",
        "responses": {
          "200": { "$ref": "#/components/responses/LinkOk" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "patch": {
        "tags": ["Smartlink"],
        "operationId": "updateLink",
        "summary": "Sửa smartlink",
        "description": "Chỉ gửi trường cần đổi; trường không gửi giữ nguyên. `targets` và `tags` thay thế toàn bộ danh sách cũ. Đổi `status` khỏi `scheduled` thì `scheduled_at` bị xoá.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkInput" } } } },
        "responses": {
          "200": { "$ref": "#/components/responses/LinkOk" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "description": "`slug_taken`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "delete": {
        "tags": ["Smartlink"],
        "operationId": "deleteLink",
        "summary": "Xoá smartlink",
        "description": "Xoá vĩnh viễn; link công khai ngừng hoạt động ngay.",
        "responses": {
          "200": {
            "description": "Đã xoá",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string" } } } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/scan": {
      "post": {
        "tags": ["Tiện ích"],
        "operationId": "scan",
        "summary": "Dò thông tin bài",
        "description": "Dò bài để điền form, chưa tạo smartlink. Tính vào giới hạn 30 lần mỗi 10 phút.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["query"],
                "properties": { "query": { "type": "string", "description": "Link Spotify / Apple Music / Deezer, mã ISRC, hoặc tên bài kèm tên nghệ sĩ." } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "type": "object", "required": ["track"], "properties": { "track": { "$ref": "#/components/schemas/Track" } } } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/uploads": {
      "post": {
        "tags": ["Tiện ích"],
        "operationId": "upload",
        "summary": "Tải ảnh bìa",
        "description": "Một ảnh PNG, JPEG, WEBP hoặc GIF, tối đa 5 MB. Gán `url` trả về vào `cover_url`.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "string", "format": "binary" } } }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "type": "object", "required": ["url"], "properties": { "url": { "type": "string" } } } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/artists/search": {
      "get": {
        "tags": ["Tiện ích"],
        "operationId": "searchArtists",
        "summary": "Tìm nghệ sĩ để gắn thẻ",
        "description": "Theo phần đầu username, tối đa 3 kết quả, trùng khớp hoàn toàn đứng đầu.",
        "parameters": [{ "name": "q", "in": "query", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": { "type": "object", "required": ["artists"], "properties": { "artists": { "type": "array", "items": { "$ref": "#/components/schemas/ArtistTag" } } } }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/analytics": {
      "get": {
        "tags": ["Tài khoản"],
        "operationId": "getAnalytics",
        "summary": "Thống kê",
        "description": "Số liệu của nghệ sĩ theo ngày (giờ Việt Nam).",
        "parameters": [{ "name": "days", "in": "query", "schema": { "type": "integer", "enum": [1, 7, 30, 90], "default": 30 } }],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Analytics" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/ProRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "webhooks": {
    "linkEvent": {
      "post": {
        "summary": "Sự kiện smartlink",
        "description": "KICK gửi tới URL khai ở Cài đặt → Tích hợp. Header: `X-Smartlink-Event`, `X-Smartlink-Delivery` (mã lần gửi, không đổi khi gửi lại), `X-Smartlink-Signature: t=<unix>,v1=<hex>` với v1 = HMAC-SHA256(secret, \"<t>.<body gốc>\"). Trả 2xx trong 10 giây; nếu lỗi KICK gửi lại sau 1 phút, 5 phút, 30 phút, 2 giờ, 6 giờ, 12 giờ, 24 giờ.",
        "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEvent" } } } },
        "responses": { "200": { "description": "Đã nhận" } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Token API của nghệ sĩ, dạng `ksm_…`." }
    },
    "responses": {
      "LinkOk": {
        "description": "OK",
        "content": { "application/json": { "schema": { "type": "object", "required": ["link"], "properties": { "link": { "$ref": "#/components/schemas/Link" } } } } }
      },
      "BadRequest": { "description": "Dữ liệu sai: `invalid_*`, `isrc_required`, `unknown_tag`, `too_many_tags`, `invalid_upload`, `upload_too_large`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unauthorized": { "description": "`invalid_token`: thiếu token, token sai hoặc đã được đổi.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "ProRequired": { "description": "`pro_required`: nghệ sĩ chưa có hoặc đã hết hạn gói Pro.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "`not_found`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": { "description": "`rate_limited`: vượt giới hạn; message cho biết thời gian chờ.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": { "code": { "type": "string", "examples": ["slug_taken"] }, "message": { "type": "string" } }
          }
        }
      },
      "Status": { "type": "string", "enum": ["public", "unlisted", "draft", "scheduled"] },
      "Target": {
        "type": "object",
        "required": ["platform", "url"],
        "properties": { "platform": { "type": "string", "examples": ["spotify"] }, "url": { "type": "string", "format": "uri" } }
      },
      "ArtistTag": {
        "type": "object",
        "required": ["username", "name", "avatar_url"],
        "properties": { "username": { "type": "string" }, "name": { "type": "string" }, "avatar_url": { "type": "string" } }
      },
      "LinkSummary": {
        "type": "object",
        "required": ["id", "url", "slug", "title", "artist_name", "cover_url", "status", "click_count", "created_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "url": { "type": "string", "format": "uri" },
          "slug": { "type": "string" },
          "title": { "type": "string" },
          "artist_name": { "type": "string" },
          "cover_url": { "type": "string" },
          "status": { "$ref": "#/components/schemas/Status" },
          "release_date": { "type": ["string", "null"], "format": "date" },
          "scheduled_at": { "type": ["string", "null"], "format": "date-time" },
          "click_count": { "type": "integer" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Link": {
        "type": "object",
        "required": ["id", "url", "slug", "title", "artist_name", "cover_url", "status", "isrc", "composer", "theme", "lyrics", "targets", "tags", "view_count", "click_count", "created_at", "updated_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "url": { "type": "string", "format": "uri", "description": "https://{username}.nhac.to/{slug}" },
          "slug": { "type": "string" },
          "title": { "type": "string" },
          "artist_name": { "type": "string" },
          "cover_url": { "type": "string" },
          "status": { "$ref": "#/components/schemas/Status" },
          "release_date": { "type": ["string", "null"], "format": "date" },
          "isrc": { "type": "string" },
          "scheduled_at": { "type": ["string", "null"], "format": "date-time" },
          "composer": { "type": "string" },
          "theme": { "type": "string", "enum": ["blur", "dark", "light"] },
          "lyrics": { "type": "string" },
          "targets": { "type": "array", "items": { "$ref": "#/components/schemas/Target" } },
          "tags": { "type": "array", "items": { "$ref": "#/components/schemas/ArtistTag" } },
          "view_count": { "type": "integer" },
          "click_count": { "type": "integer" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "LinkInput": {
        "type": "object",
        "properties": {
          "title": { "type": "string", "minLength": 1, "maxLength": 200, "description": "Bắt buộc khi tạo, trừ khi chỉ gửi isrc." },
          "slug": { "type": "string", "maxLength": 100, "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$", "description": "Bỏ trống khi tạo thì tạo từ tên bài." },
          "status": { "$ref": "#/components/schemas/Status" },
          "artist_name": { "type": "string", "maxLength": 200 },
          "isrc": { "type": "string", "description": "12 ký tự; bắt buộc khi status là scheduled." },
          "scheduled_at": { "type": "string", "format": "date-time", "description": "Bắt buộc khi status là scheduled; ở tương lai, kèm múi giờ." },
          "release_date": { "type": "string", "description": "YYYY-MM-DD; chuỗi rỗng để xoá." },
          "cover_url": { "type": "string", "description": "Link https:// hoặc url từ /uploads." },
          "targets": { "type": "array", "items": { "$ref": "#/components/schemas/Target" }, "description": "Mỗi nền tảng tối đa một link, đúng domain của nền tảng." },
          "tags": { "type": "array", "maxItems": 10, "items": { "type": "string" }, "description": "Username nghệ sĩ trên KICK, có hoặc không có @." },
          "lyrics": { "type": "string", "maxLength": 20000 },
          "composer": { "type": "string", "maxLength": 200 },
          "theme": { "type": "string", "enum": ["blur", "dark", "light"], "default": "blur" }
        }
      },
      "Track": {
        "type": "object",
        "required": ["title", "artist", "cover_url", "release_date", "lyrics", "targets"],
        "properties": {
          "title": { "type": "string" },
          "artist": { "type": "string" },
          "cover_url": { "type": "string" },
          "release_date": { "type": "string" },
          "lyrics": { "type": "string" },
          "targets": {
            "type": "array",
            "items": {
              "allOf": [{ "$ref": "#/components/schemas/Target" }, { "type": "object", "properties": { "confidence": { "type": "integer", "minimum": 0, "maximum": 100 } } }]
            }
          }
        }
      },
      "MeResponse": {
        "type": "object",
        "required": ["workspace", "token_id"],
        "properties": {
          "workspace": {
            "type": "object",
            "required": ["id", "name", "username", "avatar_url", "url"],
            "properties": {
              "id": { "type": "string", "format": "uuid" },
              "name": { "type": "string" },
              "username": { "type": "string" },
              "avatar_url": { "type": "string" },
              "url": { "type": "string", "format": "uri" }
            }
          },
          "token_id": { "type": "string", "format": "uuid" }
        }
      },
      "Count": { "type": "object", "required": ["key", "count"], "properties": { "key": { "type": "string" }, "count": { "type": "integer" } } },
      "Analytics": {
        "type": "object",
        "required": ["days", "totals", "daily", "top_links", "platforms", "countries", "cities", "devices", "referrers"],
        "properties": {
          "days": { "type": "integer" },
          "totals": {
            "type": "object",
            "properties": {
              "views": { "type": "integer" },
              "visitors": { "type": "integer", "description": "Người xem không trùng (không lưu IP)" },
              "clicks": { "type": "integer" },
              "new_fans": { "type": "integer" },
              "fans": { "type": "integer" },
              "links": { "type": "integer" }
            }
          },
          "daily": {
            "type": "array",
            "items": { "type": "object", "properties": { "day": { "type": "string", "format": "date" }, "views": { "type": "integer" }, "clicks": { "type": "integer" } } }
          },
          "top_links": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string", "format": "uuid" },
                "title": { "type": "string" },
                "cover_url": { "type": "string" },
                "views": { "type": "integer" },
                "clicks": { "type": "integer" }
              }
            }
          },
          "platforms": { "type": "array", "items": { "$ref": "#/components/schemas/Count" } },
          "countries": { "type": "array", "items": { "$ref": "#/components/schemas/Count" } },
          "cities": { "type": "array", "items": { "$ref": "#/components/schemas/Count" } },
          "devices": { "type": "array", "items": { "$ref": "#/components/schemas/Count" } },
          "referrers": { "type": "array", "items": { "$ref": "#/components/schemas/Count" } }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "required": ["event", "created_at", "workspace_id", "data"],
        "properties": {
          "event": { "type": "string", "enum": ["link.created", "link.updated", "link.deleted", "link.ready", "webhook.test"] },
          "created_at": { "type": "string", "format": "date-time" },
          "workspace_id": { "type": "string", "format": "uuid" },
          "data": { "description": "Object smartlink; link.deleted là { id, slug }; webhook.test là { message }.", "type": "object" }
        }
      }
    }
  }
}
