{
  "openapi": "3.1.0",
  "info": {
    "title": "KYTScore demo API",
    "version": "0.1.0-preview",
    "summary": "USDT source-of-funds check (prototype)",
    "description": "KYTScore is a USDT source-of-funds checker that traces where an address's USDT came from — up to 2 hops back over the last 90 days on TRON, BNB Smart Chain and Ethereum — and flags links to OFAC-sanctioned, Tether / Circle-frozen, hacked or scam addresses, with the evidence path, amounts and dates.\n\nThis is the rate-limited endpoint behind the demo at https://kytscore.com/. Every request must carry a Cloudflare Turnstile token (`turnstile`) from the widget on that page; tokens are single-use and are also required for cached results, so the endpoint only works from the web page. Requests without a valid token get 403 `captcha_required`. A key-based API for programmatic use is not open yet: leave an email at https://kytscore.com/#price-h to hear when it opens (preview pricing: 100 checks a month free, then $0.02–0.05 per check). It is a prototype: no SLA and the response shape may change.\n\nData freshness: OFAC SDN, Tether / Circle freezes and public labels are looked up live at check time; if that lookup fails, a local snapshot is used. `data_source` says which (live, snapshot, or mixed when only some addresses fell back), `live_index` gives the live lists' sync times and `snapshot` the snapshot times.\n\nLimits: 10 uncached checks per hour per IP address, 200 per day in total (UTC), at most 30 upstream data calls per check; a result is cached for 24 hours and cached hits don't count. Exposure describes on-chain fund flows only, not ownership or wrongdoing.",
    "contact": {
      "url": "https://kytscore.com/#report"
    }
  },
  "servers": [
    {
      "url": "https://kytscore.com"
    }
  ],
  "paths": {
    "/api/kyt/check": {
      "post": {
        "operationId": "checkAddress",
        "summary": "Trace an address's USDT sources (≤ 2 hops, 90 days) and score its exposure",
        "description": "Reads up to 400 incoming USDT transfers of the address over the last 90 days (transfers under $1 ignored), labels every sender, expands up to 8 unidentified senders one more hop, and scores the exposure to sanctioned, frozen, hacked, scam and mixer addresses.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "address",
                  "turnstile"
                ],
                "properties": {
                  "turnstile": {
                    "type": "string",
                    "description": "Cloudflare Turnstile token from the widget on https://kytscore.com/ (single use)"
                  },
                  "address": {
                    "type": "string",
                    "description": "TRON (T…) or EVM (0x…) address",
                    "example": "TXfVkD7ahL9ZkRc3mRkYWwQCGcGXshjF92"
                  },
                  "chain": {
                    "type": "string",
                    "enum": [
                      "tron",
                      "bsc",
                      "eth"
                    ],
                    "description": "Optional for TRON addresses; for EVM addresses defaults to bsc"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid address / chain",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "captcha_required: missing, invalid, reused or foreign-site Turnstile token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or the demo's daily data budget reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Chain data provider unavailable; try again in a minute",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "invalid_address",
                  "captcha_required",
                  "rate_limited",
                  "upstream_unavailable",
                  "internal",
                  "method_not_allowed",
                  "not_found"
                ]
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        }
      },
      "Label": {
        "type": "object",
        "properties": {
          "entity": {
            "type": "string",
            "example": "Binance"
          },
          "category": {
            "type": "string",
            "description": "exchange, hub, sanctioned, frozen, hack, scam, mixer, bridge, other …"
          },
          "subtype": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string",
            "description": "Label source id, e.g. ofac-sdn, tether-freeze, por-binance, rule:hub, ext:tronscan"
          },
          "layer": {
            "type": "string",
            "enum": [
              "local",
              "rule",
              "external"
            ],
            "description": "local = list / snapshot, rule = inferred from flows, external = third-party tag (attributed)"
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "0–1, inferred and external labels only"
          }
        }
      },
      "PathNode": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "entity": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "layer": {
            "type": "string"
          },
          "usd": {
            "type": "number",
            "description": "Total this hop sent downstream (USD)"
          },
          "ts": {
            "type": "integer",
            "description": "Time of the largest transfer (ms since epoch)"
          },
          "tx": {
            "type": "string",
            "description": "Hash of the largest transfer"
          }
        },
        "required": [
          "address"
        ]
      },
      "Seen": {
        "type": "object",
        "properties": {
          "entity": {
            "type": "string",
            "example": "Binance"
          },
          "category": {
            "type": "string",
            "description": "exchange, hub, sanctioned, frozen, hack, scam, mixer, bridge, other …"
          },
          "subtype": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string",
            "description": "Label source id, e.g. ofac-sdn, tether-freeze, por-binance, rule:hub, ext:tronscan"
          },
          "layer": {
            "type": "string",
            "enum": [
              "local",
              "rule",
              "external"
            ],
            "description": "local = list / snapshot, rule = inferred from flows, external = third-party tag (attributed)"
          },
          "confidence": {
            "type": [
              "number",
              "null"
            ],
            "description": "0–1, inferred and external labels only"
          },
          "address": {
            "type": "string"
          },
          "hop": {
            "type": "integer",
            "enum": [
              1,
              2
            ]
          },
          "percent": {
            "type": "number",
            "description": "Share of the checked address's 90-day inflow, %"
          }
        }
      },
      "CheckResult": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "chain": {
            "type": "string",
            "enum": [
              "tron",
              "bsc",
              "eth"
            ]
          },
          "asset": {
            "type": "string",
            "const": "USDT"
          },
          "window_days": {
            "type": "integer",
            "example": 90
          },
          "score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "risk_level": {
            "type": "string",
            "enum": [
              "Low",
              "Moderate",
              "High",
              "Severe"
            ],
            "description": "Low 0–30, Moderate 31–70, High 71–90, Severe 91–100"
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Which scoring rule fired, e.g. self:sanctioned, hop1>=10%, hop2>0, self:exchange-cap"
          },
          "risk_detail": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "entity": {
                  "type": "string"
                },
                "risk_type": {
                  "type": "string",
                  "enum": [
                    "sanctioned_entity",
                    "frozen_by_issuer",
                    "illicit_activity",
                    "mixer",
                    "other"
                  ]
                },
                "category": {
                  "type": "string"
                },
                "exposure_type": {
                  "type": "string",
                  "enum": [
                    "direct",
                    "indirect"
                  ]
                },
                "direction": {
                  "type": "string",
                  "enum": [
                    "incoming",
                    "outgoing"
                  ]
                },
                "hop_num": {
                  "type": "integer",
                  "enum": [
                    1,
                    2
                  ]
                },
                "volume": {
                  "type": "number",
                  "description": "Estimated USD attributed to the checked address"
                },
                "percent": {
                  "type": "number",
                  "description": "% of the 90-day inflow (outflow for outgoing)"
                },
                "source": {
                  "type": "string"
                },
                "layer": {
                  "type": "string"
                },
                "path": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PathNode"
                  },
                  "description": "checked address ← sender ← risky address"
                }
              }
            }
          },
          "notes": {
            "type": "array",
            "description": "Hints that do not change the score: frozen_exchange_upstream (a sender is a known exchange wallet that was later frozen), exchange_cap (the checked address is a known exchange wallet; score capped at Low)",
            "items": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "frozen_exchange_upstream",
                    "exchange_cap"
                  ]
                },
                "address": {
                  "type": "string"
                },
                "entity": {
                  "type": "string"
                },
                "frozen": {
                  "type": "string"
                },
                "frozen_at": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "hop": {
                  "type": "integer"
                },
                "direction": {
                  "type": "string"
                },
                "percent": {
                  "type": "number"
                }
              }
            }
          },
          "self": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Label"
            },
            "description": "Labels on the checked address itself"
          },
          "exchanges": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Seen"
            }
          },
          "hubs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Seen"
            }
          },
          "coverage": {
            "type": "object",
            "properties": {
              "inflow_usd": {
                "type": "number"
              },
              "transfers": {
                "type": "integer"
              },
              "senders": {
                "type": "integer"
              },
              "labeled_pct": {
                "type": "number"
              },
              "traced_pct": {
                "type": "number"
              },
              "unknown_pct": {
                "type": "number"
              },
              "expanded": {
                "type": "integer"
              },
              "hop2_senders": {
                "type": "integer"
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "budget:* or unavailable:* when some upstream data was missing"
          },
          "stats": {
            "type": "object",
            "properties": {
              "calls": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                }
              },
              "calls_total": {
                "type": "integer",
                "description": "Chain data calls (TronGrid / Alchemy / TRONSCAN)"
              },
              "ms": {
                "type": "integer"
              },
              "cached": {
                "type": "boolean"
              },
              "checked_at": {
                "type": "string",
                "format": "date-time"
              },
              "intel": {
                "type": "object",
                "description": "Live list lookups for this check",
                "properties": {
                  "source": {
                    "type": "string",
                    "enum": [
                      "live",
                      "snapshot",
                      "mixed"
                    ]
                  },
                  "calls": {
                    "type": "integer"
                  },
                  "ms": {
                    "type": "integer",
                    "description": "Total time of the live lookups"
                  },
                  "max_ms": {
                    "type": "integer"
                  },
                  "fallback": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Why some or all lookups used the snapshot: timeout, http, bad_response, network, breaker_open, daily_cap, per_check_cap, disabled"
                  }
                }
              }
            }
          },
          "data_source": {
            "type": "string",
            "enum": [
              "live",
              "snapshot",
              "mixed"
            ],
            "description": "Where sanctions, freezes and public labels came from: live lookup at check time, the local snapshot, or mixed"
          },
          "live_index": {
            "type": [
              "object",
              "null"
            ],
            "description": "Sync times of the live lists (live / mixed only)",
            "properties": {
              "frozen_synced_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "ofac_synced_at": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "snapshot": {
            "type": "object",
            "description": "Local snapshot times (used when data_source is snapshot or mixed; por = exchange proof-of-reserves, always local)",
            "properties": {
              "frozen": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "ofac": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "labels": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "por": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        },
        "required": [
          "address",
          "chain",
          "score",
          "risk_level",
          "risk_detail",
          "coverage",
          "data_source"
        ]
      }
    }
  }
}