---
id: overrides
title: 'Terminology Overrides'
description: 'api/localization and api/localizations — read and write the terminology overrides that sit at the top of the localization resolution stack.'
sidebar_label: 'Terminology Overrides'
sidebar_position: 11
tags: ['api', 'localization', 'terminology', 'overrides']
---

[`loc-2.v1`](/apis/loc-2-v1) is the read side of localization. This is the write side.

The [resolution stack](/apis/loc-2-v1/template-sets) ends with a terminology store that **beats
every template file**. These two endpoints are how entries get into that store — they are the
supported way to re-term Agility through the API, and what they write is what `loc-2.v1`
subsequently serves.

| Endpoint | Scope | Privilege |
| --- | --- | --- |
| `api/localization` | One key at a time | Authenticated |
| `api/localizations` | Batch read and write | **`ManageConfig`** |

## One Key at a Time — `api/localization`

A single route handling four verbs.

```http
GET    ~/api/localization?key=<tag>&language=<lang>
POST   ~/api/localization?key=<tag>&value=<text>&language=<lang>
PUT    ~/api/localization?key=<tag>&value=<text>&language=<lang>
DELETE ~/api/localization?key=<tag>&language=<lang>
```

`language` is optional and defaults to the two-letter language of the current UI culture.

**`POST` and `PUT` do the same thing** — both upsert. There is no create-versus-replace
distinction. `DELETE` removes the override, restoring whatever the template stack resolves to.

Both writes refresh the localizer immediately.

:::warning `GET` returns a bare string, and a missing override is not a 404

The response body is the override text itself — not JSON, not an object. When no override
exists, the endpoint returns **200 with an empty body**, not a 404.

An empty response is therefore ambiguous: it means either "no override is set" or "an override
is set to the empty string". If that distinction matters, use the batch endpoint, which returns
a keyed object where an absent key is distinguishable from an empty value.

:::

## Batch — `api/localizations`

Note the **plural**. Different route, different privilege, different shape.

Requires the **`ManageConfig`** privilege, which is stricter than the singular route.

```http
GET  ~/api/localizations
POST ~/api/localizations
```

:::note Both verbs take a request body, including `GET`

This is unusual and easy to miss. `GET` here is a read that takes a JSON body describing which
keys to read, not a plain URL fetch. Some HTTP clients and proxies will not send a body on a
`GET`; if reads come back empty for no obvious reason, that is the first thing to check.

:::

### Request

```json
{
  "lang": "en",
  "keys": ["Story", "Defect"],
  "overrides": [
    { "key": "Story", "value": "Backlog Item", "act": "set" },
    { "key": "Defect", "act": "delete" }
  ]
}
```

| Field | Description |
| --- | --- |
| `lang` | Language to operate on. Defaults to the current UI culture's two-letter language |
| `keys` | Keys to read back |
| `overrides` | Changes to apply. `POST` only |
| `overrides[].act` | `set` or `delete`. **Defaults to `set`** when omitted |

### Response

```json
{
  "lang": "en",
  "deleted": ["Defect"],
  "overridesByKey": { "Story": "Backlog Item" }
}
```

## Unknown Keys Are Rejected

`api/localizations` **validates the shape of the payload strictly**. Any top-level key that is
not `lang`, `overrides` or `keys` is an error.

| Error message | Cause |
| --- | --- |
| `You must supply a payload.` | Empty body |
| `Unknown payload at <name>` | An unrecognised top-level key |
| `overrides must be an array` | `overrides` is not an array |
| `Values must be strings` | A non-string `value` |
| `act must be "set" or "delete"` | A non-string `act` |
| `A value must be supplied when setting` | `act: "set"` with no `value` |

All surface as **400**, carrying `{ "errorMessage": … }`. Any unexpected server-side exception
is converted to a 400 in the same shape, so a 400 here is not always a validation failure.

:::tip Strictness is not consistent across Agility

Two endpoints reject unknown keys — this one and [`api/webhook`](/apis/webhooks/create). Others,
including the [Bulk API](/apis/asset-bulk), tolerate them.

Do not carry an assumption either way between surfaces. A payload with a stray field that works
against one endpoint can be a hard 400 against another.

:::


## Related

- [Localization overview](/apis/loc-2-v1) — the read side
- [Template sets](/apis/loc-2-v1/template-sets) — where overrides sit in the resolution stack
- [Hot reload](/apis/loc-2-v1/hot-reload) — why a change may not be visible to clients immediately
