---
name: pdf-translate
description: 通过译档公开 API 上传、估算、确认、查询、下载 PDF 翻译任务，以及查看账户和用量。Use when a user asks to translate PDFs through the YiDoc public API from OpenClaw, Hermes, Codex, Claude Code, scripts, or automation.
---

# PDF Translate API

Use the YiDoc public API when the user wants to translate a PDF through the hosted service instead of running the local CLI. The API flow is asynchronous: upload the PDF, create an estimate, confirm the translation with an idempotency key, poll status, then download the artifact.

## Prerequisites

- `YD_API_KEY`: YiDoc API key from the API management page.
- `YIDOC_API_BASE`: optional API base URL. Default to `https://translate.qkfintech.cn`.
- `jq`: used by the shell examples to extract IDs.
- A readable local PDF path.

```bash
export YD_API_KEY="yd_sk_live_***"
export YIDOC_API_BASE="${YIDOC_API_BASE:-https://translate.qkfintech.cn}"
```

Never print the full API key, commit it to a repository, or paste it into logs. If the user provides a full key in chat, avoid repeating it back.

## Public API Endpoints

- `GET /v1/me`: check the current key, account summary, and available credits.
- `POST /v1/files`: upload a PDF and receive `file_id`.
- `POST /v1/translation-estimates`: estimate tokens and credits for a file.
- `POST /v1/translations`: confirm an estimate and enqueue the translation. Must include `Idempotency-Key`.
- `GET /v1/translations/{translation_id}`: poll status, tokens, credits, errors, and artifacts.
- `GET /v1/translations/{translation_id}/events`: read progress events.
- `GET /v1/translations/{translation_id}/artifacts/{artifact_id}`: download a translated artifact.
- `GET /v1/usage`: inspect API/Web usage attribution.

## Workflow

1. Confirm the PDF path exists before calling the API.
2. Call `GET /v1/me` to verify the key and account can be read.
3. Upload the PDF with `POST /v1/files`.
4. Create an estimate with `POST /v1/translation-estimates`.
5. If `can_afford` is false, stop and tell the user to recharge credits before confirming.
6. Confirm the task with `POST /v1/translations` and a stable `Idempotency-Key`.
7. Poll `GET /v1/translations/{translation_id}` until `status` is `succeeded` or `failed`.
8. Optionally call `GET /v1/translations/{translation_id}/events` to show detailed progress.
9. If the task succeeded and `artifacts` is non-empty, download the first artifact with `GET /v1/translations/{translation_id}/artifacts/{artifact_id}`.
10. Use `GET /v1/usage` when the user asks about spending, API/Web attribution, or per-key usage.

Do not retry `POST /v1/translations` with a different JSON body under the same `Idempotency-Key`. That returns `idempotency_conflict` and can confuse job ownership.

## cURL Example

```bash
set -euo pipefail

PDF_PATH="${PDF_PATH:-demo.pdf}"
TARGET_LANGUAGE="${TARGET_LANGUAGE:-zh-CN}"
MODE="${MODE:-preserve-layout}"
JOB_KEY="${JOB_KEY:-pdf-translate-$(date +%s)}"
export YIDOC_API_BASE="${YIDOC_API_BASE:-https://translate.qkfintech.cn}"

curl -sS \
  -H "Authorization: Bearer $YD_API_KEY" \
  "$YIDOC_API_BASE/v1/me" | jq .

FILE_ID=$(curl -sS -X POST "$YIDOC_API_BASE/v1/files" \
  -H "Authorization: Bearer $YD_API_KEY" \
  -F "file=@${PDF_PATH}" | jq -r '.file_id')

ESTIMATE_JSON=$(curl -sS -X POST "$YIDOC_API_BASE/v1/translation-estimates" \
  -H "Authorization: Bearer $YD_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"file_id\":\"$FILE_ID\",\"target_language\":\"$TARGET_LANGUAGE\",\"mode\":\"$MODE\"}")

echo "$ESTIMATE_JSON" | jq .
if [ "$(echo "$ESTIMATE_JSON" | jq -r '.can_afford')" != "true" ]; then
  echo "insufficient credits for this translation" >&2
  exit 1
fi

ESTIMATE_ID=$(echo "$ESTIMATE_JSON" | jq -r '.estimate_id')

TRANSLATION_ID=$(curl -sS -X POST "$YIDOC_API_BASE/v1/translations" \
  -H "Authorization: Bearer $YD_API_KEY" \
  -H "Idempotency-Key: $JOB_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"estimate_id\":\"$ESTIMATE_ID\"}" | jq -r '.translation_id')

while true; do
  STATUS_JSON=$(curl -sS \
    -H "Authorization: Bearer $YD_API_KEY" \
    "$YIDOC_API_BASE/v1/translations/$TRANSLATION_ID")
  STATUS=$(echo "$STATUS_JSON" | jq -r '.status')
  echo "$STATUS_JSON" | jq '{translation_id,status,estimated_tokens,actual_tokens,estimated_credits,actual_credits,error_code,artifacts}'

  if [ "$STATUS" = "succeeded" ]; then
    break
  fi
  if [ "$STATUS" = "failed" ] || [ "$STATUS" = "canceled" ]; then
    echo "translation did not succeed" >&2
    exit 1
  fi
  sleep 3
done

curl -sS \
  -H "Authorization: Bearer $YD_API_KEY" \
  "$YIDOC_API_BASE/v1/translations/$TRANSLATION_ID/events" | jq .

DOWNLOAD_URL=$(echo "$STATUS_JSON" | jq -r '.artifacts[0].download_url // empty')
if [ -z "$DOWNLOAD_URL" ]; then
  echo "translation succeeded but no artifacts were returned" >&2
  exit 1
fi

curl -L \
  -H "Authorization: Bearer $YD_API_KEY" \
  "$YIDOC_API_BASE$DOWNLOAD_URL" \
  -o translated.pdf

curl -sS \
  -H "Authorization: Bearer $YD_API_KEY" \
  "$YIDOC_API_BASE/v1/usage?groupBy=overview" | jq .
```

## Error Handling

- `invalid_api_key`: ask the user to check the key value or create a new key.
- `api_key_disabled`: ask the user to re-enable or rotate the key.
- `insufficient_credits`: stop before confirming a task and ask the user to recharge.
- `idempotency_key_required`: generate a stable key for `POST /v1/translations`.
- `idempotency_conflict`: use a new key or repeat the exact same body.
- `file_too_large`: ask the user to split or compress the PDF.
- `unsupported_pdf` or `ocr_required`: report that this API version needs a text-extractable PDF.
- `artifact_not_found`: re-check the task status and artifacts before downloading.

## Output

When the workflow succeeds, return:

- `translation_id`
- final `status`
- estimated and actual tokens/credits when present
- local path to `translated.pdf`
- any recent events that explain the task progress
