{"openapi": "3.1.0", "info": {"title": "Fandan Publishing API / 放单 API", "version": "1.1.0", "description": "CDK-only workflow. No website account required. Existing v1 publishing requests remain compatible. Times are Unix milliseconds."}, "servers": [{"url": "https://chataiapi.online/fandan/api/v1"}], "security": [{"PublishingCDK": []}], "paths": {"/cdk": {"get": {"operationId": "getCard", "summary": "卡密余量 / Card credits", "description": "", "parameters": [], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CardStatus"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}}}, "/takers": {"get": {"operationId": "listTakers", "summary": "可接单用户 / Eligible takers", "description": "", "parameters": [{"name": "page", "in": "query", "schema": {"type": "integer", "minimum": 1, "default": 1}, "description": ""}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TakerPage"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}}}, "/online-takers": {"get": {"operationId": "onlineTakers", "summary": "在线人数 / Legacy online count", "description": "", "parameters": [], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OnlineCount"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}}}, "/orders": {"post": {"operationId": "publishOrder", "summary": "发布订单 / Publish order", "description": "201 creates and consumes one credit; identical retries return 200 without resetting the deadline. Unclaimed orders expire after 5 minutes: status=failed, failureReason=unclaimed_timeout, credit refunded. claimDeadlineAt and expiredAt are Unix milliseconds. Changed request content: 409; removed original: 410. POST accepts X-Fandan-CDK or ownerCdk in JSON. At least one is required and values must match when both are supplied.", "parameters": [], "responses": {"201": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OrderResult"}}}}, "200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OrderResult"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PublishBody"}}}}, "security": [{"PublishingCDK": []}, {}]}, "get": {"operationId": "listOrders", "summary": "订单列表 / Card-scoped orders", "description": "", "parameters": [{"name": "page", "in": "query", "schema": {"type": "integer", "minimum": 1, "default": 1}, "description": ""}, {"name": "status", "in": "query", "schema": {"enum": ["open", "claimed", "submitted", "completed", "failed", "withdrawn"]}, "description": ""}, {"name": "requestId", "in": "query", "schema": {"type": "string"}, "description": ""}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OrderPage"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}}}, "/orders/{id}": {"get": {"operationId": "getOrder", "summary": "查询订单 / Order detail", "description": "", "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,100}$"}}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OrderResult"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}}}, "/orders/{id}/withdraw": {"post": {"operationId": "withdrawOrder", "summary": "撤回并退次 / Withdraw and refund", "description": "Only unclaimed orders. Repeated withdrawal never refunds twice. POST accepts X-Fandan-CDK or ownerCdk in JSON. At least one is required and values must match when both are supplied.", "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,100}$"}}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OrderResult"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CardBody"}}}}, "security": [{"PublishingCDK": []}, {}]}}, "/orders/{id}/delete": {"post": {"operationId": "deleteOrder", "summary": "删除记录 / Delete record", "description": "Only withdrawn, completed or failed orders. Repeated API deletion is idempotent. No refund on delete. POST accepts X-Fandan-CDK or ownerCdk in JSON. At least one is required and values must match when both are supplied.", "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,100}$"}}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeleteResult"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CardBody"}}}}, "security": [{"PublishingCDK": []}, {}]}}, "/calls": {"get": {"operationId": "getCallStatus", "summary": "呼叫状态 / Call status", "description": "", "parameters": [], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CallStatus"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}}, "post": {"operationId": "callTakers", "summary": "呼叫指定用户 / Call selected takers", "description": "Explicit recipients required. 60-second caller and recipient cooldowns. No credit charge; 202 means queued, not read. POST accepts X-Fandan-CDK or ownerCdk in JSON. At least one is required and values must match when both are supplied.", "parameters": [], "responses": {"202": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CallResult"}}}}, "200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CallResult"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CallBody"}}}}, "security": [{"PublishingCDK": []}, {}]}}, "/calls/{id}": {"get": {"operationId": "getCall", "summary": "查询通知结果 / Call job", "description": "", "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,100}$"}}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CallResult"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}}}, "/chat": {"get": {"operationId": "getChat", "summary": "公共聊天 / Public quick chat", "description": "Last 10 in-memory messages. If cursor expired, reset=true; restart clears history. Public, not a private confirmation channel.", "parameters": [{"name": "after", "in": "query", "schema": {"type": "string"}, "description": ""}], "responses": {"200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatFeed"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}}, "post": {"operationId": "sendChat", "summary": "发送快捷消息 / Send quick message", "description": "Publisher kinds only. 2 seconds between new messages per CDK. Identical retries never resend. POST accepts X-Fandan-CDK or ownerCdk in JSON. At least one is required and values must match when both are supplied.", "parameters": [], "responses": {"201": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatResult"}}}}, "200": {"description": "Success", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatResult"}}}}, "default": {"description": "Error; inspect HTTP status and errorCode", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatBody"}}}}, "security": [{"PublishingCDK": []}, {}]}}}, "components": {"securitySchemes": {"PublishingCDK": {"type": "apiKey", "in": "header", "name": "X-Fandan-CDK"}}, "schemas": {"Error": {"type": "object", "properties": {"error": {"type": "string"}, "errorCode": {"type": "string"}, "errorEn": {"type": "string"}}, "required": ["error", "errorCode"]}, "RequestId": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,64}$", "description": "Stable ID per card and operation. Reuse exact content on retries."}, "Card": {"type": "object", "properties": {"role": {"const": "owner"}, "remaining": {"type": "integer"}, "used": {"type": "integer"}, "canPublish": {"type": "boolean"}}, "required": ["role", "remaining", "used", "canPublish"]}, "CardStatus": {"type": "object", "properties": {"cdk": {"$ref": "#/components/schemas/Card"}, "updatedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}}, "required": ["cdk", "updatedAt"]}, "TargetIds": {"type": ["array", "null"], "minItems": 1, "maxItems": 1000, "items": {"type": "string", "minLength": 1, "maxLength": 128}, "description": "Omitted/null means all takers. Nonempty list restricts visibility and claiming."}, "Order": {"type": "object", "properties": {"createdAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "updatedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "claimDeadlineAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "expiredAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "autoSubmitStartedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "autoSubmitFinishedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "claimedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "submittedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "completedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "failedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "withdrawnAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "refundedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "id": {"type": "string"}, "status": {"enum": ["open", "claimed", "submitted", "completed", "failed", "withdrawn"]}, "publishRequestId": {"type": ["string", "null"]}, "credentialEmail": {"type": ["string", "null"]}, "takerName": {"type": ["string", "null"]}, "failureReason": {"type": ["string", "null"]}, "autoSubmitErrorCode": {"type": ["string", "null"]}, "targetTakerIds": {"$ref": "#/components/schemas/TargetIds"}, "targetTakerNames": {"type": "array", "items": {"type": "string"}}, "refunded": {"type": "boolean"}, "canWithdraw": {"type": "boolean"}, "canDelete": {"type": "boolean"}, "plusCheck": {"anyOf": [{"type": "null"}, {"type": "object", "properties": {"ok": {"type": ["boolean", "null"]}, "isPlus": {"type": ["boolean", "null"]}, "checkedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "errorCode": {"type": ["string", "null"]}}, "required": []}]}}, "required": ["id", "status"]}, "OrderResult": {"type": "object", "properties": {"order": {"$ref": "#/components/schemas/Order"}, "cdk": {"$ref": "#/components/schemas/Card"}, "replayed": {"type": "boolean"}}, "required": ["order"]}, "OrderPage": {"type": "object", "properties": {"orders": {"type": "array", "items": {"$ref": "#/components/schemas/Order"}}, "total": {"type": "integer"}, "page": {"type": "integer"}, "pageSize": {"const": 50}}, "required": ["orders", "total", "page", "pageSize"]}, "PublishBody": {"type": "object", "properties": {"ownerCdk": {"type": "string"}, "requestId": {"$ref": "#/components/schemas/RequestId"}, "paymentUrl": {"type": "string", "format": "uri"}, "accessToken": {"type": "string", "minLength": 1}, "targetTakerIds": {"$ref": "#/components/schemas/TargetIds"}}, "required": ["requestId", "paymentUrl", "accessToken"]}, "DeleteResult": {"type": "object", "properties": {"ok": {"type": "boolean"}, "replayed": {"type": "boolean"}}, "required": ["ok", "replayed"]}, "Taker": {"type": "object", "properties": {"id": {"type": "string"}, "name": {"type": "string"}, "online": {"type": "boolean"}, "canTakeOrders": {"type": "boolean"}, "canReceiveCall": {"type": "boolean"}, "callCooldownUntil": {"type": ["integer", "null"], "description": "Unix milliseconds"}}, "required": ["id", "name", "online", "canTakeOrders", "canReceiveCall", "callCooldownUntil"]}, "TakerPage": {"type": "object", "properties": {"takers": {"type": "array", "items": {"$ref": "#/components/schemas/Taker"}}, "total": {"type": "integer"}, "page": {"type": "integer"}, "pageSize": {"const": 50}, "ttlMs": {"type": "integer"}, "updatedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "callCooldownUntil": {"type": ["integer", "null"], "description": "Unix milliseconds"}}, "required": ["takers", "total", "page", "pageSize"]}, "OnlineCount": {"type": "object", "properties": {"onlineTakers": {"type": "integer"}, "ttlMs": {"type": "integer"}, "updatedAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}}, "required": ["onlineTakers", "ttlMs", "updatedAt"]}, "CallJob": {"type": "object", "properties": {"total": {"type": "integer"}, "pending": {"type": "integer"}, "sent": {"type": "integer"}, "failed": {"type": "integer"}, "skipped": {"type": "integer"}, "unknown": {"type": "integer"}, "id": {"type": "string"}, "createdAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "mode": {"type": "string"}}, "required": ["id", "createdAt", "mode", "total", "pending", "sent", "failed", "skipped", "unknown"]}, "CallResult": {"type": "object", "properties": {"job": {"$ref": "#/components/schemas/CallJob"}, "replayed": {"type": "boolean"}, "mode": {"type": "string"}}, "required": ["job"]}, "CallStatus": {"type": "object", "properties": {"role": {"const": "owner"}, "mode": {"type": "string"}, "connection": {"type": "string"}, "botUsername": {"type": "string"}, "recipientCount": {"type": "integer"}, "cooldownUntil": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "recipients": {"type": "array", "items": {"type": "object", "properties": {"id": {"type": "string"}, "maskedName": {"type": "string"}, "enabled": {"type": "boolean"}, "cooldownUntil": {"type": ["integer", "null"], "description": "Unix milliseconds"}}, "required": ["id", "maskedName", "enabled", "cooldownUntil"]}}, "job": {"anyOf": [{"$ref": "#/components/schemas/CallJob"}, {"type": "null"}]}}, "required": ["role", "recipients", "cooldownUntil", "job"]}, "CallBody": {"type": "object", "properties": {"ownerCdk": {"type": "string"}, "requestId": {"$ref": "#/components/schemas/RequestId"}, "recipientIds": {"type": "array", "minItems": 1, "maxItems": 1000, "items": {"type": "string", "minLength": 1, "maxLength": 128}}}, "required": ["requestId", "recipientIds"]}, "LocalizedText": {"type": "object", "properties": {"zh": {"type": "string"}, "en": {"type": "string"}}, "required": ["zh", "en"]}, "Message": {"type": "object", "properties": {"id": {"type": "string"}, "role": {"enum": ["owner", "taker"]}, "kind": {"type": "string"}, "senderName": {"type": "string"}, "createdAt": {"type": ["integer", "null"], "description": "Unix milliseconds"}, "text": {"$ref": "#/components/schemas/LocalizedText"}}, "required": ["id", "role", "createdAt", "text"]}, "ChatBody": {"type": "object", "properties": {"ownerCdk": {"type": "string"}, "requestId": {"$ref": "#/components/schemas/RequestId"}, "kind": {"enum": ["ask", "generating_qr", "publishing", "thanks"]}}, "required": ["requestId", "kind"]}, "ChatResult": {"type": "object", "properties": {"message": {"$ref": "#/components/schemas/Message"}, "replayed": {"type": "boolean"}}, "required": ["message", "replayed"]}, "ChatFeed": {"type": "object", "properties": {"messages": {"type": "array", "items": {"$ref": "#/components/schemas/Message"}}, "cursor": {"type": ["string", "null"]}, "reset": {"type": "boolean"}, "scope": {"const": "public"}, "retention": {"const": "memory"}, "options": {"type": "object", "additionalProperties": {"$ref": "#/components/schemas/LocalizedText"}}, "onlineTakers": {"type": "array", "items": {"type": "string"}}}, "required": ["messages", "cursor", "reset", "scope", "retention", "options", "onlineTakers"]}, "CardBody": {"type": "object", "properties": {"ownerCdk": {"type": "string"}}, "required": []}}}}