DocWise.ai API 与 MCP 服务器
最后更新:2026-11-01
DocWise.ai API 让你在自己的代码里翻译文档,质量档位、价格和限制都与网页版相同。远程 MCP 服务器把同样的能力提供给 AI 智能体(Claude、Cursor 及其他 MCP 客户端)。
API 属于 Pro 和 Ultra 方案。机器可读的规范:/api/v1/openapi.json(OpenAPI 3.1)。
快速开始
- 在 Pro 或 Ultra 方案下打开 API 密钥 并创建一个密钥。密钥以
sk_开头,只显示一次:请立即保存到你的密钥管理处。 - 上传、翻译、下载:
export DOCWISE_KEY=sk_...
API=https://docwise.ai/api/v1
# 1. Declare the file: you get a presigned upload URL
curl -s -X POST $API/documents \
-H "Authorization: Bearer $DOCWISE_KEY" -H "Content-Type: application/json" \
-d '{"filename":"report.pdf","bytes":'$(wc -c < report.pdf)',"mime":"application/pdf"}'
# → {"document":{"id":"DOC_ID",...},"method":"PUT","url":"https://…","expires_in":3600}
# 2. Upload the bytes straight to storage (curl sets Content-Length)
curl -s -X PUT -T report.pdf "UPLOAD_URL"
# 3. Finish: the file is read and priced
curl -s -X POST $API/documents/DOC_ID/complete -H "Authorization: Bearer $DOCWISE_KEY"
# → {"document":{...,"pages":12},"quote":{"pages":12,"tiers":[{"tier":"standard","credits":24,...}],...}}
# 4. Translate (credits are held now, settled when the job ends)
curl -s -X POST $API/translations \
-H "Authorization: Bearer $DOCWISE_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: report-de-2026-11-01" \
-d '{"document_id":"DOC_ID","tgt_lang":"de","tier":"standard"}'
# → {"id":"JOB_ID","status":"queued","billing":{"held_credits":24,...},...}
# 5. Poll until done, then get download links (valid 5 minutes)
curl -s $API/translations/JOB_ID -H "Authorization: Bearer $DOCWISE_KEY"
curl -s $API/translations/JOB_ID/outputs -H "Authorization: Bearer $DOCWISE_KEY"
认证
每个请求都带上密钥:Authorization: Bearer sk_...。密钥属于你的账户,只能看到你自己的文档和翻译;别人的 id 一律返回 404。请从服务器调用 API,不要在网页里调用:API 不发送 CORS 头,也从不接受 Cookie。
- 密钥丢了?在 API 密钥页删除它并新建一个。删除的密钥立即失效(
401)。 - 订阅结束后,你的密钥会返回
403 plan_required,重新订阅后恢复。
翻译流程
POST /documents,传filename、精确字节数bytes和mime。支持的格式和大小限制与网页版相同(GET /formats)。16 MiB 以内返回一个url;更大的文件返回parts,按顺序分别上传到各自的 URL,每段part_size字节(最后一段较短)。- 在
expires_in秒(1 小时)内用完全相同的Content-LengthPUT上传字节。链接过期了?调用POST /documents/{id}/upload-urls。 POST /documents/{id}/complete读取文档并返回{ document, quote }。quote.tiers[]列出每个档位的积分价格,以及在你的方案中是否available。之后可用GET /documents/{id}/quote重新报价。POST /translations,传document_id、tgt_lang(取自GET /languages)、tier,可选glossary_id、src_lang、outputs和trial: true(只翻译前 3 页;之后对同一文档、语言和档位做完整翻译时,这几页不再收费)。- 每 2–5 秒轮询
GET /translations/{id}。status依次为queued → parsing → translating → reviewing → rendering → done(或failed、canceled);progress从 0 到 1。 GET /translations/{id}/outputs为每个输出文件返回一个预签名链接,有效期 5 分钟;GET /translations/{id}/zip把全部文件打包成一个 ZIP 流式返回。
POST /translations/{id}/cancel 取消任务;DELETE /documents/{id} 立即删除文件及所有输出(进行中的任务会被取消并退款)。
积分与档位
1 积分 = 快速档 1 页。一页是一页 PDF 或一张幻灯片,或 2,000 个字符的文本。
economy(快速):每页 1 积分standard(均衡):每页 2 积分pro(最佳):每页 5 积分flagship(专家):每页 10 积分,仅 Ultra 方案
创建翻译时冻结 页数 × 每页积分。任务结束时按实际交付扣费,绝不超过冻结额;失败或取消的任务全额退回。每个翻译的 billing 显示 held_credits、charged_credits 和 refunded_credits。
幂等
调用 POST /translations 时发送 Idempotency-Key: <8–128 个字母、数字或 . _ : ->。24 小时内用同一个键、同样的请求体重试,会返回同一个翻译(响应头 Idempotency-Replayed: true),绝不会重复冻结积分。同一个键配不同的请求体返回 409 idempotency_conflict。失败的请求没有任何效果,可以用同一个键重试。
限制
- 文件: Pro 和 Ultra 支持最大 200 MB 的文件、最多 1,000 页的文档。文档保留 30 天(Pro)或 90 天(Ultra)。
- 频率限制按 API 密钥和操作计:每 200 毫秒一次(账户、语言、格式),300 毫秒(列表、文档、报价),500 毫秒(翻译状态、输出),1 秒(上传、完成、翻译、取消、删除、ZIP)。超出返回
429 too_many_requests,Retry-After头给出等待秒数。 - 同时最多 10 个未完成的上传。下载链接有效 5 分钟,上传链接 1 小时。
错误
所有错误都是带真实 HTTP 状态码的 JSON:
{ "error": { "code": "insufficient_credits", "message": "Not enough credits" } }
400 invalid_request:字段缺失或格式不对(消息里会写明字段)401 unauthorized:没有密钥,或密钥错误、已删除、已过期(同一 IP 的失败尝试会被限速:200 毫秒内重试返回429)402 insufficient_credits:请充值或换便宜的档位403 plan_required:账户不是 Pro 或 Ultra;403 tier_unavailable:你的方案不含该档位404 not_found:你的账户里没有这个 id409 upload_incomplete、document_not_ready、not_ready、idempotency_conflict、idempotency_in_progress413 file_too_large、415 unsupported_format、422 too_many_pages、file_encrypted、file_corrupt429 too_many_requests、too_many_uploads5xx internal、engine_timeout:稍后重试;未能启动的任务已退回积分
Python
import os, time, requests
API = "https://docwise.ai/api/v1"
H = {"Authorization": f"Bearer {os.environ['DOCWISE_KEY']}"}
def call(method, path, **kw):
while True:
r = requests.request(method, API + path, headers={**H, **kw.pop("headers", {})}, **kw)
if r.status_code != 429:
break
time.sleep(int(r.headers.get("Retry-After", "1")))
if r.status_code >= 400:
raise RuntimeError(r.json()["error"])
return r.json()
data = open("report.pdf", "rb").read()
up = call("POST", "/documents", json={"filename": "report.pdf", "bytes": len(data), "mime": "application/pdf"})
requests.put(up["url"], data=data).raise_for_status() # files ≤ 16 MiB: one PUT
doc = call("POST", f"/documents/{up['document']['id']}/complete")
print("pages:", doc["quote"]["pages"])
job = call("POST", "/translations", json={"document_id": doc["document"]["id"], "tgt_lang": "de", "tier": "standard"},
headers={"Idempotency-Key": f"report-de-{doc['document']['id']}"})
while job["status"] not in ("done", "failed", "canceled"):
time.sleep(3)
job = call("GET", f"/translations/{job['id']}")
for out in call("GET", f"/translations/{job['id']}/outputs")["outputs"]:
open(out["filename"], "wb").write(requests.get(out["url"]).content)
JavaScript(Node 18+)
import { readFile, writeFile } from 'node:fs/promises';
const API = 'https://docwise.ai/api/v1';
const H = { Authorization: `Bearer ${process.env.DOCWISE_KEY}` };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function call(method, path, body, headers = {}) {
for (;;) {
const res = await fetch(API + path, {
method,
headers: { ...H, ...headers, ...(body ? { 'Content-Type': 'application/json' } : {}) },
body: body ? JSON.stringify(body) : undefined,
});
if (res.status === 429) { await sleep(1000 * Number(res.headers.get('retry-after') || 1)); continue; }
const json = await res.json();
if (!res.ok) throw new Error(`${json.error.code}: ${json.error.message}`);
return json;
}
}
const bytes = await readFile('report.pdf');
const up = await call('POST', '/documents', { filename: 'report.pdf', bytes: bytes.length, mime: 'application/pdf' });
await fetch(up.url, { method: 'PUT', body: bytes });
const { document, quote } = await call('POST', `/documents/${up.document.id}/complete`);
console.log('credits on Balanced:', quote.tiers.find((t) => t.tier === 'standard').credits);
let job = await call('POST', '/translations', { document_id: document.id, tgt_lang: 'de', tier: 'standard' },
{ 'Idempotency-Key': `report-de-${document.id}` });
while (!['done', 'failed', 'canceled'].includes(job.status)) {
await sleep(3000);
job = await call('GET', `/translations/${job.id}`);
}
const { outputs } = await call('GET', `/translations/${job.id}/outputs`);
for (const out of outputs)
await writeFile(out.filename, Buffer.from(await (await fetch(out.url)).arrayBuffer()));
MCP 服务器
端点:https://docwise.ai/api/mcp(Streamable HTTP),用同一个 API 密钥作为 Bearer 令牌。工具:get_account、list_languages、list_documents、upload_document、complete_upload、quote_document、translate_document、get_translation、get_download_links、cancel_translation、list_glossaries。
upload_document 接受最大 10 MB 的 base64 文件内容;更大的文件会返回预签名上传链接,由你的客户端 PUT 上传,再调用 complete_upload。服务器从不抓取你提供的 URL。智能体会被要求在 translate_document 之前先和你确认积分花费。
Claude Code
claude mcp add --transport http docwise https://docwise.ai/api/mcp \
--header "Authorization: Bearer sk_..."
Cursor(~/.cursor/mcp.json,或项目里的 .cursor/mcp.json)
{
"mcpServers": {
"docwise": {
"url": "https://docwise.ai/api/mcp",
"headers": { "Authorization": "Bearer sk_..." }
}
}
}
Claude Desktop(设置 → 开发者 → 编辑配置,claude_desktop_config.json;通过 mcp-remote 桥接,需要 Node.js)
{
"mcpServers": {
"docwise": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://docwise.ai/api/mcp", "--header", "Authorization:${DOCWISE_AUTH}"],
"env": { "DOCWISE_AUTH": "Bearer sk_..." }
}
}
}
不要把密钥提交到共享仓库:优先使用环境变量或客户端自带的密钥存储。