> ## Documentation Index
> Fetch the complete documentation index at: https://docs.holydocs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Translations API

REST API endpoints for managing AI-powered documentation translations.

## Overview

The Translations API provides programmatic control over AI-powered documentation translations. Configure target languages, trigger translation jobs, manage glossary terms for domain-specific terminology, and track translation status across your documentation.

**Base path:** `https://api.holydocs.com/api/v1/projects/:projectId`

All translation endpoints require authentication. Language management and triggering translations both require the `projects:write` scope.

HolyDocs translations are AI-powered, not machine-translated. Each page is translated by an LLM that understands context, preserves MDX component structure, and respects your glossary for consistent terminology. Code blocks, component props, and URLs are preserved untranslated.

## Supported Languages

HolyDocs supports 24 languages for AI-powered translation:

**European**

English (en), French (fr), German (de), Spanish (es), Portuguese (pt), Italian (it), Dutch (nl), Polish (pl), Swedish (sv), Norwegian (no), Danish (da), Finnish (fi)
  **Asian**

Chinese Simplified (zh-CN), Chinese Traditional (zh-TW), Japanese (ja), Korean (ko), Thai (th), Vietnamese (vi), Indonesian (id), Malay (ms)
  **Other**

Arabic (ar), Hebrew (he), Turkish (tr), Ukrainian (uk)

The source language for your documentation is auto-detected from your content. You do not need to specify it explicitly. English is the default source language.

## List Languages

Retrieve the configured target languages for a project.

```bash
GET /api/v1/projects/:projectId/languages
```

Copy as context

**cURL**

```bash
    curl "https://api.holydocs.com/api/v1/projects/$PROJECT_ID/languages" \
      -H "Authorization: Bearer $HOLYDOCS_API_KEY"
    ```
  **CLI**

```bash
    holydocs api get "/projects/$PROJECT_ID/languages"
    ```
  **SDK**

```ts
    import { HolyDocs } from '@holydocs/sdk';
    const client = new HolyDocs({ apiKey: process.env.HOLYDOCS_API_KEY });
    const { data } = await client.translations.listLanguages(projectId);
    ```

### Response

```json
{
  "data": {
    "sourceLanguage": "en",
    "targetLanguages": [
      {
        "code": "ja",
        "name": "Japanese",
        "translatedPages": 38,
        "totalPages": 42,
        "completionRate": 0.90,
        "lastTranslatedAt": "2026-04-10T08:30:00Z"
      },
      {
        "code": "de",
        "name": "German",
        "translatedPages": 42,
        "totalPages": 42,
        "completionRate": 1.0,
        "lastTranslatedAt": "2026-04-11T02:15:00Z"
      },
      {
        "code": "zh-CN",
        "name": "Chinese Simplified",
        "translatedPages": 20,
        "totalPages": 42,
        "completionRate": 0.48,
        "lastTranslatedAt": "2026-04-08T14:00:00Z"
      }
    ]
  }
}
```

**Field reference**

| Field | Type | Description |
|---|---|---|
| `sourceLanguage` | string | ISO 639-1 language code of the source content |
| `targetLanguages[].code` | string | ISO 639-1 language code (with optional region) |
| `targetLanguages[].name` | string | Human-readable language name |
| `targetLanguages[].translatedPages` | number | Number of pages with completed translations |
| `targetLanguages[].totalPages` | number | Total pages in the project |
| `targetLanguages[].completionRate` | number | Translation progress (0.0 - 1.0) |
| `targetLanguages[].lastTranslatedAt` | string | ISO 8601 timestamp of last translation activity |

**cURL**

```bash
    curl "https://api.holydocs.com/api/v1/projects/proj_abc123/languages" \
      -H "Authorization: Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890"
    ```
  **JavaScript**

```javascript
    const response = await fetch(
      'https://api.holydocs.com/api/v1/projects/proj_abc123/languages',
      {
        headers: {
          'Authorization': 'Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890'
        }
      }
    );
    const { data } = await response.json();

    for (const lang of data.targetLanguages) {
      const pct = (lang.completionRate * 100).toFixed(0);
      console.log(`${lang.name}: ${pct}% (${lang.translatedPages}/${lang.totalPages})`);
    }
    ```

## Add Language

Add a new target language to the project. This does not trigger translation -- it configures the language as a translation target.

```bash
POST /api/v1/projects/:projectId/languages
```

### Request Body

```json
{
  "code": "fr",
  "autoTranslate": true
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | ISO 639-1 language code from the supported languages list |
| `autoTranslate` | boolean | No | Automatically translate new and updated pages (default: `false`) |

### Response

```json
{
  "data": {
    "code": "fr",
    "name": "French",
    "autoTranslate": true,
    "translatedPages": 0,
    "totalPages": 42,
    "completionRate": 0.0,
    "createdAt": "2026-04-11T10:00:00Z"
  }
}
```

Enable `autoTranslate` to keep translations up-to-date automatically. When a source page is updated and redeployed, the translation for that page is automatically re-queued. This is the recommended setting for actively maintained documentation.

## Remove Language

Remove a language and all its translations from the project.

```bash
DELETE /api/v1/projects/:projectId/languages/:code
```

### Path Parameters

| Parameter | Type | Description |
|---|---|---|
| `projectId` | string | Project ID |
| `code` | string | Language code to remove (e.g., `fr`) |

### Response

```json
{
  "data": {
    "deleted": true,
    "code": "fr",
    "pagesDeleted": 42
  }
}
```

This permanently deletes all translations for the removed language. The source content is not affected. This action cannot be undone.

## Trigger Translation

Start a translation job for a specific page or all pages in a language.

```bash
POST /api/v1/projects/:projectId/translate
```

### Request Body

**Translate all pages**

```json
    {
      "language": "ja",
      "scope": "all"
    }
    ```
  **Translate a single page**

```json
    {
      "language": "ja",
      "scope": "page",
      "pagePath": "/quickstart"
    }
    ```
  **Retranslate stale pages**

```json
    {
      "language": "ja",
      "scope": "stale"
    }
    ```

| Field | Type | Required | Description |
|---|---|---|---|
| `language` | string | Yes | Target language code |
| `scope` | `all` \| `page` \| `stale` | No | What to translate (default: `all`) |
| `pagePath` | string | Conditional | Required when `scope` is `page` |
| `force` | boolean | No | Retranslate even if a current translation exists (default: `false`) |

### Response

```json
{
  "data": {
    "jobId": "tr_abc123",
    "language": "ja",
    "scope": "all",
    "pagesQueued": 42,
    "estimatedDuration": 180,
    "status": "queued",
    "createdAt": "2026-04-11T10:00:00Z"
  }
}
```

**cURL**

```bash
    curl -X POST "https://api.holydocs.com/api/v1/projects/proj_abc123/translate" \
      -H "Authorization: Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890" \
      -H "Content-Type: application/json" \
      -d '{
        "language": "ja",
        "scope": "all"
      }'
    ```
  **JavaScript**

```javascript
    const response = await fetch(
      'https://api.holydocs.com/api/v1/projects/proj_abc123/translate',
      {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          language: 'ja',
          scope: 'stale' // Only retranslate pages updated since last translation
        })
      }
    );
    const { data } = await response.json();
    console.log(`Translation job ${data.jobId}: ${data.pagesQueued} pages queued`);
    ```

### Translation Scopes

| Scope | Behavior |
|---|---|
| `all` | Translate every page, skipping pages with up-to-date translations (unless `force: true`) |
| `page` | Translate a single page specified by `pagePath` |
| `stale` | Only translate pages where the source content has changed since the last translation |

## List Translations

Retrieve translation status for all pages in a specific language.

```bash
GET /api/v1/projects/:projectId/translations
```

### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `language` | string | Yes | Target language code |
| `status` | `completed` \| `pending` \| `failed` \| `stale` | No | Filter by translation status |
| `page` | number | No | Page number (default: 1) |
| `perPage` | number | No | Results per page (default: 50, max: 200) |

### Response

```json
{
  "data": [
    {
      "pagePath": "/quickstart",
      "pageTitle": "Quickstart Guide",
      "language": "ja",
      "status": "completed",
      "translatedTitle": "\u30af\u30a4\u30c3\u30af\u30b9\u30bf\u30fc\u30c8\u30ac\u30a4\u30c9",
      "sourceHash": "a1b2c3d4",
      "translatedHash": "e5f6g7h8",
      "wordCount": 1240,
      "translatedAt": "2026-04-10T08:30:00Z",
      "sourceUpdatedAt": "2026-04-09T12:00:00Z"
    },
    {
      "pagePath": "/api/authentication",
      "pageTitle": "API Authentication",
      "language": "ja",
      "status": "stale",
      "translatedTitle": "API\u8a8d\u8a3c",
      "sourceHash": "i9j0k1l2",
      "translatedHash": "m3n4o5p6",
      "wordCount": 890,
      "translatedAt": "2026-04-05T06:00:00Z",
      "sourceUpdatedAt": "2026-04-08T14:00:00Z"
    },
    {
      "pagePath": "/webhooks",
      "pageTitle": "Webhooks",
      "language": "ja",
      "status": "pending",
      "translatedTitle": null,
      "sourceHash": "q7r8s9t0",
      "translatedHash": null,
      "wordCount": 1560,
      "translatedAt": null,
      "sourceUpdatedAt": "2026-04-10T09:00:00Z"
    }
  ],
  "meta": {
    "total": 42,
    "page": 1,
    "perPage": 50,
    "totalPages": 1
  }
}
```

### Translation Statuses

| Status | Description |
|---|---|
| `completed` | Translation is current and matches the latest source content |
| `stale` | Translation exists but the source page has been updated since. The translation is still served but may be outdated. |
| `pending` | Page has not been translated yet for this language |
| `in_progress` | Translation is currently being processed |
| `failed` | Translation failed (typically due to content complexity or LLM error) |

A `stale` translation is still served to readers. HolyDocs does not hide translated content when the source changes. Instead, it flags the translation as stale so you can prioritize retranslation. Use `scope: "stale"` in the translate endpoint to batch-update stale pages.

## Add Glossary Term

Add a term to the project glossary. Glossary terms ensure consistent translation of domain-specific terminology, product names, and technical jargon across all languages.

```bash
POST /api/v1/projects/:projectId/glossary
```

### Request Body

```json
{
  "term": "HolyDocs",
  "description": "Product name, do not translate",
  "translations": {
    "ja": "HolyDocs",
    "de": "HolyDocs",
    "zh-CN": "HolyDocs",
    "fr": "HolyDocs"
  }
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `term` | string | Yes | The source term (1-100 characters) |
| `description` | string | No | Context for the translator (e.g., "Product name, do not translate") |
| `translations` | object | No | Map of language codes to translated terms. If omitted for a language, the AI uses the description as guidance. |
| `caseSensitive` | boolean | No | Match the term case-sensitively (default: `true`) |

### Response

```json
{
  "data": {
    "id": "gl_abc123",
    "term": "HolyDocs",
    "description": "Product name, do not translate",
    "translations": {
      "ja": "HolyDocs",
      "de": "HolyDocs",
      "zh-CN": "HolyDocs",
      "fr": "HolyDocs"
    },
    "caseSensitive": true,
    "createdAt": "2026-04-11T10:00:00Z"
  }
}
```

**cURL**

```bash
    curl -X POST "https://api.holydocs.com/api/v1/projects/proj_abc123/glossary" \
      -H "Authorization: Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890" \
      -H "Content-Type: application/json" \
      -d '{
        "term": "deployment pipeline",
        "description": "The automated build and publish process",
        "translations": {
          "ja": "\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3",
          "de": "Deployment-Pipeline",
          "fr": "pipeline de d\u00e9ploiement"
        }
      }'
    ```
  **JavaScript**

```javascript
    const response = await fetch(
      'https://api.holydocs.com/api/v1/projects/proj_abc123/glossary',
      {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          term: 'deployment pipeline',
          description: 'The automated build and publish process',
          translations: {
            ja: '\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3',
            de: 'Deployment-Pipeline',
            fr: 'pipeline de d\u00e9ploiement'
          }
        })
      }
    );
    const { data } = await response.json();
    console.log(`Glossary term "${data.term}" added (${data.id})`);
    ```

### Glossary Best Practices

**Product and brand names**

Add entries for your product name, feature names, and brand terminology. Set the translation to the original term if it should not be translated (e.g., "HolyDocs" stays "HolyDocs" in all languages).
  **Technical terminology**

Terms like "webhook," "API key," and "SSO" may have established translations in some languages and should remain in English in others. Use the `translations` field to specify the correct form per language.
  **Acronyms and abbreviations**

Add glossary entries for acronyms (e.g., "RBAC" with description "Role-Based Access Control") so the AI translator expands or preserves them appropriately in context.
  **UI labels**

If your documentation references specific UI labels (e.g., "Settings > API Keys"), add glossary entries so these labels are translated consistently with your product's actual localized UI.

## Update Glossary Term

Update an existing glossary term's translations, context, or `doNotTranslate` flag. All fields are optional — unspecified fields are left untouched.

```bash
PUT /api/v1/projects/:projectId/glossary/:termId
```

### Request Body

```json
{
  "sourceTerm": "deployment pipeline",
  "translations": {
    "es": "pipeline de despliegue",
    "ja": "デプロイパイプライン"
  },
  "context": "technical",
  "doNotTranslate": false
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `sourceTerm` | string | No | New source term |
| `translations` | object | No | Map of language codes to translated terms (replaces the existing map) |
| `context` | string | No | Context for the translator |
| `doNotTranslate` | boolean | No | If `true`, preserve the source term verbatim in all translations |

### Response

```json
{
  "data": {
    "id": "gl_abc123",
    "sourceTerm": "deployment pipeline",
    "translations": {
      "es": "pipeline de despliegue",
      "ja": "デプロイパイプライン"
    },
    "context": "technical",
    "doNotTranslate": false
  }
}
```

Returns `404 NOT_FOUND` if the glossary term does not exist or belongs to another project.

## List Glossary Terms

Retrieve all glossary terms for a project.

```bash
GET /api/v1/projects/:projectId/glossary
```

### Query Parameters

| Parameter | Type | Required | Description |
|---|---|---|---|
| `page` | number | No | Page number (default: 1) |
| `perPage` | number | No | Results per page (default: 50, max: 200) |
| `q` | string | No | Search glossary terms by name |

### Response

```json
{
  "data": [
    {
      "id": "gl_abc123",
      "term": "HolyDocs",
      "description": "Product name, do not translate",
      "translations": {
        "ja": "HolyDocs",
        "de": "HolyDocs",
        "zh-CN": "HolyDocs",
        "fr": "HolyDocs"
      },
      "caseSensitive": true,
      "createdAt": "2026-04-11T10:00:00Z"
    },
    {
      "id": "gl_def456",
      "term": "deployment pipeline",
      "description": "The automated build and publish process",
      "translations": {
        "ja": "\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3",
        "de": "Deployment-Pipeline",
        "fr": "pipeline de d\u00e9ploiement"
      },
      "caseSensitive": false,
      "createdAt": "2026-04-11T10:05:00Z"
    }
  ],
  "meta": {
    "total": 15,
    "page": 1,
    "perPage": 50,
    "totalPages": 1
  }
}
```

## Export Translations to Repo

Commit all translations for a given language to a new branch in the project's connected repo. Lets you turn dashboard-only translations into repo-committed files that downstream tools or teammates can review in a pull request.

```bash
POST /api/v1/projects/:projectId/translations/export-repo
```

### Request Body

```json
{
  "language": "ja",
  "branch": "translations/ja"
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `language` | string | Yes | Target language code |
| `branch` | string | No | Destination branch name. Defaults to `holydocs/translations-<lang>-<timestamp>` |

### Response

```json
{
  "data": {
    "branch": "holydocs/translations-ja-1713384000000",
    "commitSha": "a1b2c3d4...",
    "filesWritten": 42
  }
}
```

### Behavior

- Files are written as `<language>/<slug>.mdx` under the project's `repoPath` (docs directory).
- If the branch doesn't exist, it's created from the default branch. Existing branches are reused (idempotent).
- The response includes the commit SHA — open a PR manually on GitHub to merge.
- Requires a GitHub connection on the project. GitLab export is not yet supported.
- Returns `filesWritten: 0` when there are no translations for the language (no-op).

Use this endpoint to graduate AI-generated or dashboard-edited translations into your repository so they are preserved across re-deploys and visible in code review. Once exported, HolyDocs treats them as human-owned translations and will never overwrite them with AI output.

## Translation Workflow

A typical translation workflow using the API:

**Add target languages**

Configure the languages you want to support. Enable `autoTranslate` for languages that should stay automatically up-to-date.

    ```bash
    POST /api/v1/projects/proj_abc123/languages
    {"code": "ja", "autoTranslate": true}
    ```
  **Build your glossary**

Add domain-specific terms, product names, and technical terminology to ensure consistent translations.

    ```bash
    POST /api/v1/projects/proj_abc123/glossary
    {"term": "HolyDocs", "description": "Product name, do not translate"}
    ```
  **Trigger initial translation**

Translate all existing pages for each configured language.

    ```bash
    POST /api/v1/projects/proj_abc123/translate
    {"language": "ja", "scope": "all"}
    ```
  **Monitor progress**

Poll the translations endpoint to track completion status.

    ```bash
    GET /api/v1/projects/proj_abc123/translations?language=ja&status=pending
    ```
  **Maintain translations**

With `autoTranslate` enabled, updated pages are retranslated on each deployment. For manual control, use `scope: "stale"` to batch-retranslate pages whose source has changed.

## Translation Limits by Plan

| Plan | Languages | Pages per Month | Glossary Terms |
|---|---|---|---|
| Free | 1 | 50 | 10 |
| Starter | 3 | 500 | 50 |
| Pro | 10 | 5,000 | 200 |
| Business | 24 | 50,000 | 1,000 |
| Enterprise | 24 | Unlimited | Unlimited |

"Pages per month" counts individual page translation operations. Translating 42 pages into 3 languages counts as 126 page translations. Retranslating a stale page also counts as one operation.

## Error Codes

| Code | Status | Description |
|---|---|---|
| `NOT_FOUND` | 404 | Project not found or language not configured |
| `VALIDATION_ERROR` | 400 | Invalid language code, missing required fields, or unsupported language |
| `AUTH_ERROR` | 401 | Missing or invalid authentication |
| `FORBIDDEN` | 403 | API key lacks `projects:write` scope |
| `LIMIT_EXCEEDED` | 429 | Language limit, monthly page limit, or glossary term limit exceeded |
| `ALREADY_EXISTS` | 409 | Language already configured or glossary term already exists |
| `TRANSLATION_FAILED` | 502 | LLM translation failed (retry the job) |