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

# TextSetu API

> Authentication, scopes, conventions, and errors for the TextSetu REST API.

The TextSetu API lets you read and write translations, import/export across every
supported format, and fetch project stats. It is the single contract every
TextSetu developer tool — the CLI, the GitHub Action / CI, the MCP server, and
SDKs — is built on.

* **Base URL:** `https://api.textsetu.com/api/v1` (self-host: `<server>/api/v1`)
* **OpenAPI spec:** [`/api/v1/openapi.json`](https://api.textsetu.com/api/v1/openapi.json) — use it for SDK/CLI/MCP codegen
* **Envelope:** success → `{ "success": true, "data": … }`; error → `{ "success": false, "error": { "code", "message" } }`

Every endpoint on the left is generated from that spec, with a **Try it** console
you can call with a real token.

## Authentication

Send your token as a bearer credential on every request:

```
Authorization: Bearer <token>
```

There are two token types:

| Token                           | Prefix       | Acts as                                                                                                         | Best for                       |
| ------------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------- | ------------------------------ |
| **Personal Access Token (PAT)** | `tsu_pat_…`  | its owner — bounded by the owner's role-based permissions, then narrowed to the permissions you grant the token | CLI, scripting, the MCP server |
| **Project token**               | `tsu_proj_…` | a machine scoped to **one** project, granting exactly the permissions you select                                | CI / GitHub Action             |

Tokens are created from the TextSetu web app (never from the API itself) and the
secret is shown **once** at creation — store it immediately. Tokens are hashed at
rest; a leaked token can be revoked at any time.

<Note>
  **Security.** Put tokens in environment variables / CI secrets — never commit
  them. Grant the least-privilege set of permissions the automation needs.
</Note>

### Permissions

Authorization is **permission-based** — there are no coarse read/write/manage
scopes. When you create a token you pick an allow-list of RBAC permission keys
(the same catalog your org's roles are built from, e.g. `translation_read`,
`translation_create`, `glossary_update`). Each endpoint declares the permission
it requires; you can see it as `x-permission` on every operation in this
reference, and the token-creation screen lets you start from a role preset
(Viewer / Editor / Admin) and fine-tune.

A token's **effective access is its underlying authority ∩ its allow-list**: a
PAT can never exceed the permissions its owner already holds, and a project token
is bound to its one project. Selecting a permission you don't hold is rejected at
creation.

## Organization-level resources

Glossaries and translation memories belong to an **organization** and are shared
across its projects, so the API splits them in two:

|                                                                                | Path                                                                                       | Works with               |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | ------------------------ |
| **Using** them — TM matches, concordance, glossary lookup/check, pre-translate | `/projects/{projectId}/tm/…`, `/projects/{projectId}/glossary/…`                           | PAT **or** project token |
| **Managing** them — creating glossaries/TMs, editing terms and entries         | `/orgs/{orgId}/glossaries`, `/glossaries/{glossaryId}/…`, `/translation-memories/{tmId}/…` | **PAT only**             |

Which glossaries and TMs apply to a project is resolved from the project's own
assignments, so the project-scoped calls need no extra configuration — this is
the path CI pipelines and AI agents should use.

Management endpoints reject a project token with `403`, because a token bound to
a single project must not mutate an asset that every other project also consumes.
Use a PAT, which carries its owner's role-based permissions.

## Rate limits

Requests are limited per token (per IP when unauthenticated). On exceeding the
limit you receive `429` with `error.code = "RATE_001"`. Default: 600 requests/min
(configurable per deployment via `API_V1_RATE_LIMIT`).

## Errors

| HTTP | `error.code`            | Meaning                                               |
| ---- | ----------------------- | ----------------------------------------------------- |
| 400  | `VAL_001`               | Validation error (see `error.details`)                |
| 401  | `AUTH_001` / `AUTH_003` | Missing/invalid token, or expired/revoked             |
| 403  | `PERM_001` / `PERM_002` | Token lacks the required permission, or wrong project |
| 404  | `RES_001`               | Not found                                             |
| 429  | `RATE_001`              | Rate limited                                          |

## Pagination

List endpoints (e.g. keys) accept `page` (default 1) and `limit` (default 50,
max 200) and return `{ keys, total, page, limit }`.

## Branch targeting

Every read and write over keys, values, stats, `POST /sources`, and
`GET /translations` accepts an optional `?branch=<branchId>` query param, which
targets an open translation branch instead of the project's main branch. The
branch must belong to the project, be open, and the project must have branching
enabled — otherwise the request is a `400`.

Branch targeting is **required** when the project protects its main branch: a
direct-to-main write on such a project is rejected with `403`, and the caller is
expected to import into a branch and merge it for review. Branch writes are plain
additive deltas — no approval workflow, no plurals (a plural value collapses to
its `other` form), and no cleanup.

## Quickstart

### Read stats

```bash theme={null}
curl -H "Authorization: Bearer $TEXTSETU_TOKEN" \
  https://api.textsetu.com/api/v1/projects/$PROJECT_ID/stats
```

### Import source strings

```bash theme={null}
curl -X POST -H "Authorization: Bearer $TEXTSETU_TOKEN" \
  -H "Content-Type: application/json" \
  https://api.textsetu.com/api/v1/projects/$PROJECT_ID/sources \
  -d '{
    "format": "json-nested",
    "files": [
      { "path": "en.json", "languageCode": "en",
        "contentBase64": "'"$(base64 < locales/en.json)"'" }
    ]
  }'
# → { "keysAdded": 12, "valuesAdded": 12, "valuesUpdated": 0, "valuesSkipped": 0 }
```

### Export approved translations

```bash theme={null}
curl -H "Authorization: Bearer $TEXTSETU_TOKEN" \
  "https://api.textsetu.com/api/v1/projects/$PROJECT_ID/translations?format=json-nested&languages=fr,de&status=approved"
# → { "files": [ { "path": "fr.json", "languageCode": "fr", "contentBase64": "…" }, … ] }
```

## Building on top of the API

Because the [OpenAPI spec](https://api.textsetu.com/api/v1/openapi.json) describes
the entire surface, downstream tools generate against it:

* **CLI** — `push`/`pull` map to `/sources` and `/translations`.
* **GitHub Action** — a project token (repo secret) + `push-sources` / `pull-translations`.
* **[MCP server](/api/mcp)** — a remote MCP server lets Claude and other AI agents manage translations in natural language; its tools proxy to these endpoints.
* **SDKs** — typed clients generated from the spec.
