---
title: "Strings API"
description: "List source strings, inspect version history, save human translations, and review selections."
canonical_url: "https://docs.linguana.dev/docs/reference/strings-api"
markdown_url: "https://docs.linguana.dev/docs/reference/strings-api.md"
x_farming_labs_generated_preamble: true
---

# Strings API
URL: /docs/reference/strings-api
LLM index: /llms.txt
Description: List source strings, inspect version history, save human translations, and review selections.

# Strings API

These endpoints power the dashboard's strings editor. They use the signed-in user's organization membership. A project build token or browser publishable key does not authorize string editing.

## Authentication and scope

Send the dashboard session cookie to the API, subject to its configured origin policy. `OWNER`, `ADMIN`, `EDITOR`, and `VIEWER` can read. `OWNER`, `ADMIN`, and `EDITOR` can write when the project and organization are active. Unauthorized access uses `404 PROJECT_NOT_FOUND` to avoid disclosing project existence.

All paths below start with `/api/v1`. `:project` is the project ID. `:message` is the string's database `id` returned by the list, not its stable `key`. `:environment` in the list accepts an environment ID or name. Writes affect project translation versions; they do not select a published environment release.

| Method and path | Purpose |
| --- | --- |
| `GET /projects/:project/environments/:environment/strings` | List strings in the latest ready build with selected target translations and language statistics. |
| `GET /projects/:project/strings/:message` | Read source context, locations, translation history, glossary matches, and memory suggestions. |
| `PUT /projects/:project/strings/:message/translations/:locale` | Save a validated human translation as a new accepted version. |
| `POST /projects/:project/strings/bulk-review` | Apply one decision to a selection of translation IDs, with per-item results. |

## List strings

```http title="Example request"
GET /api/v1/projects/PROJECT_ID/environments/development/strings?locale=fr,de&status=review&limit=50
```

| Query | Contract |
| --- | --- |
| `locale` | Optional comma-separated project target locale codes. Defaults to enabled target locales. Unknown codes, including the source locale, return `400 LOCALE_NOT_IN_PROJECT`. |
| `status` | `all` (default), `missing`, `review`, `rejected`, or `accepted`. `accepted` requires acceptance in every selected locale; other status filters match at least one selected locale. |
| `q` | Case-insensitive search in the stable key or source text. Trimmed; maximum 200 characters. It does not search target translations. |
| `file` | Source-location file filter; maximum 1,000 characters. |
| `cursor` | Opaque pagination value from `nextCursor`. Keep the same scope and filters while paging. |
| `limit` | Integer 1–200; defaults to 50. |

The response contains:

| Field | Meaning |
| --- | --- |
| `strings` | Rows sorted by stable key. Each has `id`, `key`, `sourceText`, `sourceLocale`, `placeholders`, `context`, `locations`, and `translations`. |
| `strings[].translations` | Map from selected locale code to its latest translation view, or `null` when missing. |
| `locales` | Per-locale totals: `code`, `total`, `translated` (accepted), `needsReview`, `rejected`, and `missing`. These are whole-build statistics, not counts for just the current page or search. |
| `total` | Number of strings matching the selected filters before pagination. |
| `manifest` | Latest ready build's `id` and `createdAt`, or `null`. |
| `nextCursor` | Cursor for the next page, or `null`. |

With no ready build, the API returns an empty `strings` array, zero statistics, `total: 0`, `manifest: null`, and `nextCursor: null`.

A translation view includes `id`, `text`, `status`, `version`, `origin`, `authorId`, `updatedAt`, and `outdated`. `outdated` compares the version's source content hash to the current string. Status values include `ACCEPTED`, `REVIEW_REQUIRED`, and `REJECTED`. None of these fields identifies what is currently live.

## Inspect a string

```http title="Example request"
GET /api/v1/projects/PROJECT_ID/strings/MESSAGE_ID?environment=development
```

`environment` is optional and accepts a project environment ID or name. When a ready build exists in that environment, locations come from it; without that build, the current implementation can fall back to the string's latest recorded build location. Without the query, the latest recorded location is used. Translation history is project-wide rather than restricted to the chosen environment.

The response has:

- `string`: source text, stable key, placeholders, context, hashes, and timestamps.
- `manifest` and `locations`: recorded build context for the source string.
- `locales`: project target locale codes and their statuses.
- `translations`: versions grouped by locale, newest first. Each includes the translation view, `approvalKind`, `jobId`, `validation`, author, and review records with reviewer, decision, note, and replacement version ID.
- `glossary`: active terms that match the source text, with target wording, locale, rule, case sensitivity, and notes.
- `suggestions`: up to five translation-memory suggestions per target locale. A `match` of `exact` or `source` describes compatibility or matching normalized source; suggestions are not automatically accepted.

## Save a human translation

```http title="Example request — replace IDs and use your session"
PUT /api/v1/projects/PROJECT_ID/strings/MESSAGE_ID/translations/fr
Content-Type: application/json
Idempotency-Key: UNIQUE_KEY_FOR_THIS_EDIT

{
  "text": "Bienvenue, {name}",
  "expectedVersion": 3,
  "note": "Use the same greeting as the sign-in screen."
}
```

The sample assumes the source contains `{name}`. Use the actual source placeholders and latest version from the read response.

| Body field | Contract |
| --- | --- |
| `text` | Required string, 1–20,000 characters. Must preserve source placeholders, ICU syntax, named rich slots, and applicable glossary rules. |
| `expectedVersion` | Optional integer, 0 or greater. Send the latest version you read; use `0` when no version exists for this string and locale. |
| `note` | Optional trimmed note, maximum 2,000 characters. Recorded with the edit and, when applicable, its review. |

The locale must be a target language added to the project. It cannot be the source locale. Saving creates a new `ACCEPTED` version with `origin: HUMAN` and `approvalKind: human_edit`; it records the author and audit event and updates translation memory. A pending or rejected predecessor receives an edit review; a pending predecessor leaves the review queue.

### Concurrent edits

When `expectedVersion` differs from the latest stored version, the API returns `409` with `code: STALE_TRANSLATION`. No replacement version is saved for that stale attempt. Keep the draft, fetch the string again, compare the latest wording, then submit the resolved edit with the new version and a new idempotency key. Omitting `expectedVersion` omits this comparison; editor clients should send it.

### Idempotency

`Idempotency-Key` is required. Retry the same logical edit with the same key and identical body after a transport failure. A key reused for different content returns `409 IDEMPOTENCY_CONFLICT`. A successful replay returns the recorded `translation`, `replay: true`, and `catalog: null`; it does not create another version or rerun catalog preparation.

A normal success returns `translation` plus `catalog` containing `ready`, per-build `results`, and optional `jobCatalogReady`. A saved edit can return `catalog.errorCode: CATALOG_REBUILD_FAILED` with `ready: false`. The translation is still saved; inspect catalog state before trying to publish. Readiness depends on the other required strings too.

Structural problems return `422 VALIDATION_FAILED` with `details.issues`. An extreme length ratio is a nonblocking warning stored with a human edit. Successful validation and acceptance do not publish a release or modify the contents of a published catalog.

## Review a selection

```http title="Example request — translation IDs, not message IDs"
POST /api/v1/projects/PROJECT_ID/strings/bulk-review
Content-Type: application/json
Idempotency-Key: UNIQUE_KEY_FOR_THIS_SELECTION

{
  "translationIds": ["TRANSLATION_ID_A", "TRANSLATION_ID_B"],
  "decision": "APPROVED",
  "note": "Checked against the checkout screen."
}
```

| Body field | Contract |
| --- | --- |
| `translationIds` | Required array of 1–200 nonempty translation IDs. Duplicate IDs are deduplicated. |
| `decision` | `APPROVED`, `REJECTED`, or `SOURCE_FALLBACK`. |
| `note` | Optional string, maximum 2,000 characters. |

Approval and source fallback create accepted replacement versions. Source fallback uses the original source text. Rejection marks the selected wording rejected. Approval validates the wording before accepting it. Each decision is recorded, and eligible catalogs are rebuilt after the batch. None of these decisions publishes a release.

The response contains `results`, `summary: { succeeded, failed }`, and `catalogs`. Each result includes `id`, `ok`, and `status`; success can include `reviewId`, `replacementTranslationId`, and `replay`, while failure includes `code` and `message`. Catalog results contain `jobs` and `locales`, and may include `errorCode: CATALOG_REBUILD_FAILED`.

The batch is not all-or-nothing. An HTTP success can contain failed items, including missing IDs, superseded versions, already accepted versions, validation failures, or concurrent write conflicts. Inspect every result and refresh failed rows. Use a new key for a changed selection or decision, and the same key for an unchanged retry.

For a single review, the dashboard also uses `POST /api/v1/translations/:translation/review`. Its body can carry `APPROVED`, `REJECTED`, `EDITED`, or `SOURCE_FALLBACK`; it also requires a session and an idempotency key. Use the `PUT` string endpoint for editor saves with `expectedVersion`.

## Errors to handle

| HTTP / code | Recovery |
| --- | --- |
| `400 INVALID_STRINGS_QUERY` | Correct locale, status, cursor, or limit filters. |
| `400 IDEMPOTENCY_KEY_REQUIRED` | Supply a key for the logical write. |
| `400 INVALID_TRANSLATION` / `INVALID_BULK_REVIEW` | Correct the request body and size limits. |
| `400 LOCALE_NOT_IN_PROJECT` | Choose a project target language. |
| `404 PROJECT_NOT_FOUND` / `ENVIRONMENT_NOT_FOUND` / `STRING_NOT_FOUND` | Check the selected scope and membership; do not assume the resource exists. |
| `409 STALE_TRANSLATION` | Reload current version, reconcile your draft, and submit with the current expected version. |
| `409 IDEMPOTENCY_CONFLICT` | Use the original body for a retry or a new key for a new action. |
| Per-item `409 TRANSLATION_SUPERSEDED` / `TRANSLATION_ALREADY_ACCEPTED` | Refresh the selection and review only current pending work. |
| `422 VALIDATION_FAILED` / per-item `STRUCTURAL_VALIDATION_FAILED` | Restore required placeholders, rich slots, ICU syntax, and glossary terms. |

API errors include `code`, `message`, `requestId`, and optional `details`. Keep the code and request ID for diagnostics.

<ExpectedResult>
An edit produces an accepted version and catalog preparation results. A stale edit is rejected. Bulk review reports individual outcomes. Published catalog delivery continues to follow the environment's release pointer, with original source text as fallback.
</ExpectedResult>

<NextStep>
[Publish a language](/docs/use-linguana/publish-language), or return to the [HTTP API](/docs/reference/http-api) for build and runtime endpoints.
</NextStep>

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
