{"openapi":"3.1.0","info":{"title":"WTF-Trans API","version":"1.0.0","summary":"Public lookup and agent annotation for English–Chinese foul-word pairs.","description":"Canonical human/agent spec: https://wtf-trans.tra.cuhk.edu.hk/docs.md. Updated 2026-09-03.","license":{"name":"UNLICENSED"}},"servers":[{"url":"https://wtf-trans.tra.cuhk.edu.hk"}],"tags":[{"name":"agent","description":"Bearer-authenticated annotator workflow"},{"name":"lookup","description":"Anonymous confirmed-pair lookup"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"wtf_*","description":"API key issued to an annotator user. Header: Authorization: Bearer wtf_…"}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}},"KeywordPair":{"type":"object","properties":{"pair_index":{"type":"integer","minimum":0},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"notes":{"type":"string","maxLength":2000},"en_start":{"type":["integer","null"]},"en_end":{"type":["integer","null"]},"zh_start":{"type":["integer","null"]},"zh_end":{"type":["integer","null"]},"source":{"type":"string"},"review_id":{"type":"integer"}}},"AgentTask":{"type":"object","properties":{"id":{"type":"integer"},"index_orig":{"type":["integer","null"]},"st":{"type":"string","description":"English source sentence"},"tt":{"type":"string","description":"Chinese target sentence"},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"pairs":{"type":"array","items":{"type":"object","properties":{"pair_index":{"type":"integer","minimum":0},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"notes":{"type":"string","maxLength":2000},"en_start":{"type":["integer","null"]},"en_end":{"type":["integer","null"]},"zh_start":{"type":["integer","null"]},"zh_end":{"type":["integer","null"]},"source":{"type":"string"},"review_id":{"type":"integer"}}}},"verdict":{"type":["string","null"],"enum":["ok","skip","no_foul","no_corresponding",null]},"due_at":{"type":["string","null"],"format":"date-time"},"ai_hint":{"type":"object","properties":{"status":{"type":["string","null"]},"confidence":{"type":["string","null"]},"verdict":{"type":["string","null"]},"en_kw":{"type":["string","null"]},"zh_kw":{"type":["string","null"]}}}}},"Verdict":{"type":"string","enum":["ok","skip","no_foul","no_corresponding"]}}},"paths":{"/api/v1/agent/me":{"get":{"tags":["agent"],"operationId":"getAgentMe","summary":"Identity and remaining assigned queue","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Authenticated annotator","content":{"application/json":{"schema":{"type":"object","required":["user_id","username","roles","pending_assigned"],"properties":{"user_id":{"type":"integer"},"username":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"pending_assigned":{"type":"integer"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"403":{"description":"User is not an annotator","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"500":{"description":"Database error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}}}}},"/api/v1/agent/tasks/claim":{"post":{"tags":["agent"],"operationId":"claimAgentTasks","summary":"Claim assigned unreviewed rows","description":"Returns up to `limit` rows assigned to the key's user with verdict IS NULL. Default 10, max 50. Does not lock rows. Rate limit 120/min.","security":[{"bearerAuth":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":"integer","minimum":1,"maximum":50,"default":10}}}}}},"responses":{"200":{"description":"Batch of tasks","content":{"application/json":{"schema":{"type":"object","required":["tasks","claimed","pending_assigned","limit"],"properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"index_orig":{"type":["integer","null"]},"st":{"type":"string","description":"English source sentence"},"tt":{"type":"string","description":"Chinese target sentence"},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"pairs":{"type":"array","items":{"type":"object","properties":{"pair_index":{"type":"integer","minimum":0},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"notes":{"type":"string","maxLength":2000},"en_start":{"type":["integer","null"]},"en_end":{"type":["integer","null"]},"zh_start":{"type":["integer","null"]},"zh_end":{"type":["integer","null"]},"source":{"type":"string"},"review_id":{"type":"integer"}}}},"verdict":{"type":["string","null"],"enum":["ok","skip","no_foul","no_corresponding",null]},"due_at":{"type":["string","null"],"format":"date-time"},"ai_hint":{"type":"object","properties":{"status":{"type":["string","null"]},"confidence":{"type":["string","null"]},"verdict":{"type":["string","null"]},"en_kw":{"type":["string","null"]},"zh_kw":{"type":["string","null"]}}}}}},"claimed":{"type":"integer"},"pending_assigned":{"type":"integer"},"limit":{"type":"integer"}}}}}},"400":{"description":"Invalid JSON","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"500":{"description":"Database error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}}}}},"/api/v1/agent/tasks/{id}":{"get":{"tags":["agent"],"operationId":"getAgentTask","summary":"Get one assigned task","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Task","content":{"application/json":{"schema":{"type":"object","required":["task"],"properties":{"task":{"type":"object","properties":{"id":{"type":"integer"},"index_orig":{"type":["integer","null"]},"st":{"type":"string","description":"English source sentence"},"tt":{"type":"string","description":"Chinese target sentence"},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"pairs":{"type":"array","items":{"type":"object","properties":{"pair_index":{"type":"integer","minimum":0},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"notes":{"type":"string","maxLength":2000},"en_start":{"type":["integer","null"]},"en_end":{"type":["integer","null"]},"zh_start":{"type":["integer","null"]},"zh_end":{"type":["integer","null"]},"source":{"type":"string"},"review_id":{"type":"integer"}}}},"verdict":{"type":["string","null"],"enum":["ok","skip","no_foul","no_corresponding",null]},"due_at":{"type":["string","null"],"format":"date-time"},"ai_hint":{"type":"object","properties":{"status":{"type":["string","null"]},"confidence":{"type":["string","null"]},"verdict":{"type":["string","null"]},"en_kw":{"type":["string","null"]},"zh_kw":{"type":["string","null"]}}}}}}}}}},"400":{"description":"Invalid id","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"404":{"description":"Not assigned to this user","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"500":{"description":"Database error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}}}},"post":{"tags":["agent"],"operationId":"submitAgentTask","summary":"Submit a verdict for an assigned unreviewed task","security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["verdict"],"properties":{"verdict":{"type":"string","enum":["ok","skip","no_foul","no_corresponding"]},"pairs":{"type":"array","items":{"type":"object","properties":{"pair_index":{"type":"integer","minimum":0},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"notes":{"type":"string","maxLength":2000},"en_start":{"type":["integer","null"]},"en_end":{"type":["integer","null"]},"zh_start":{"type":["integer","null"]},"zh_end":{"type":["integer","null"]},"source":{"type":"string"},"review_id":{"type":"integer"}}}}}}}}},"responses":{"200":{"description":"Stored","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"task":{"type":"object","properties":{"id":{"type":"integer"},"index_orig":{"type":["integer","null"]},"st":{"type":"string","description":"English source sentence"},"tt":{"type":"string","description":"Chinese target sentence"},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"pairs":{"type":"array","items":{"type":"object","properties":{"pair_index":{"type":"integer","minimum":0},"en_kw":{"type":"string"},"zh_kw":{"type":"string"},"notes":{"type":"string","maxLength":2000},"en_start":{"type":["integer","null"]},"en_end":{"type":["integer","null"]},"zh_start":{"type":["integer","null"]},"zh_end":{"type":["integer","null"]},"source":{"type":"string"},"review_id":{"type":"integer"}}}},"verdict":{"type":["string","null"],"enum":["ok","skip","no_foul","no_corresponding",null]},"due_at":{"type":["string","null"],"format":"date-time"},"ai_hint":{"type":"object","properties":{"status":{"type":["string","null"]},"confidence":{"type":["string","null"]},"verdict":{"type":["string","null"]},"en_kw":{"type":["string","null"]},"zh_kw":{"type":["string","null"]}}}}}}}}}},"400":{"description":"Invalid verdict or incomplete pairs","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"404":{"description":"Not assigned to this user","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"409":{"description":"Already submitted","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"500":{"description":"Database error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}}}}},"/api/v1/lookup":{"get":{"tags":["lookup"],"operationId":"lookupTranslations","summary":"Confirmed EN↔ZH keyword matches","description":"Anonymous. Rate limit 60/min/IP.","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","maxLength":100}},{"name":"from","in":"query","required":false,"schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Matches","content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string"},"from":{"type":"string","enum":["en","zh"]},"category":{"type":["string","null"]},"matches":{"type":"array","items":{"type":"object","properties":{"en":{"type":"string"},"zh":{"type":"string"},"count":{"type":"integer"}}}}}}}}},"400":{"description":"Invalid query","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"500":{"description":"Database error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}}}}},"/api/v1/examples":{"get":{"tags":["lookup"],"operationId":"lookupExamples","summary":"Confirmed sentence examples for a keyword","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","maxLength":100}},{"name":"from","in":"query","required":false,"schema":{"type":"string","enum":["en","zh"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"maximum":50}},{"name":"cursor","in":"query","required":false,"schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Example rows","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"st":{"type":"string"},"tt":{"type":"string"},"en_kw":{"type":"string"},"zh_kw":{"type":"string"}}}},"nextCursor":{"type":["integer","null"]}}}}}},"400":{"description":"Invalid query","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"500":{"description":"Database error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}}}}},"/api/v1/keywords":{"get":{"tags":["lookup"],"operationId":"listPublicKeywords","summary":"Confirmed keywords by language","description":"Anonymous. Rate limit 30/min/IP.","parameters":[{"name":"lang","in":"query","required":false,"schema":{"type":"string","enum":["en","zh"],"default":"en"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"maximum":500}}],"responses":{"200":{"description":"Keywords","content":{"application/json":{"schema":{"type":"object","properties":{"lang":{"type":"string","enum":["en","zh"]},"keywords":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"count":{"type":"integer"}}}}}}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"500":{"description":"Database error","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}}}}}}}