# Set the general-ledger mapping

> The exports.gl_mapping.set operation: PUT /api/v1/exports/gl_mapping in the REST API and the exports_gl_mapping_set tool in the MCP server.

Replaces the legal entity's general-ledger mapping in the mode of the request with the rules given (at most
200\). Each rule maps Ownrecord ledger accounts to an account of the entity's own chart: `account` is an account
kind (`processor_clearing`, `bank`, `tax_payable`, `revenue`, `processor_fees`, …), an account code (e.g.
`bank:ent_…:EUR`) or a code prefix ending in `*` (e.g. `tax_payable:EU_OSS_UNION:*`); `glAccount` is the
general-ledger code and `glName` its name. Exports requested afterwards use the new mapping; earlier exports
keep theirs. Pass the `version` you read as `expectedVersion` to avoid overwriting someone else's change.

## At a glance

- **REST:** `PUT /api/v1/exports/gl_mapping`
- **MCP tool:** `exports_gl_mapping_set`
- **Operation ID:** `exports.gl_mapping.set`
- **Scope:** Legal entity
- **Permission:** `ledger:write`
- **Risk tier:** `write`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` optional
- **Modes:** Test and live
- **Authentication:** API key with the `ledger:write` scope
- **Since:** M8

## Request

### Body

A JSON object (`Content-Type: application/json`):

| Field               | Type                 | Required | Description                                                                                  |
| ------------------- | -------------------- | -------- | -------------------------------------------------------------------------------------------- |
| `rules`             | array of object      | Yes      | The whole table: it replaces the current rules.                                              |
| `rules[].account`   | string               | Yes      |                                                                                              |
| `rules[].glAccount` | string               | Yes      |                                                                                              |
| `rules[].glName`    | string               | No       |                                                                                              |
| `expectedVersion`   | integer (at least 0) | No       | The mapping's `version` you read (0 while none is set): a guard against a concurrent change. |

## Response

`200 OK` with a JSON object:

| Field               | Type              | Description                                                                            |
| ------------------- | ----------------- | -------------------------------------------------------------------------------------- |
| `object`            | `"gl_mapping"`    |                                                                                        |
| `entityId`          | `ent_…` ID        |                                                                                        |
| `livemode`          | boolean           |                                                                                        |
| `rules`             | array of object   |                                                                                        |
| `rules[].account`   | string            | An account kind (e.g. `tax_payable`), an account code, or a code prefix ending in `*`. |
| `rules[].glAccount` | string            | The account in the entity's own chart of accounts.                                     |
| `rules[].glName`    | string or null    |                                                                                        |
| `version`           | integer           | 0 while no mapping is set; pass it as `expectedVersion` when you change it.            |
| `updatedAt`         | date-time or null |                                                                                        |
| `updatedBy`         | string or null    |                                                                                        |

## Examples

### Map clearing, bank, VAT, revenue and fees

```http
PUT /api/v1/exports/gl_mapping
Authorization: Bearer or_test_…
Content-Type: application/json

{
  "rules": [
    {
      "account": "processor_clearing",
      "glAccount": "1360",
      "glName": "Stripe clearing"
    },
    {
      "account": "bank",
      "glAccount": "1200",
      "glName": "Bank"
    },
    {
      "account": "tax_payable",
      "glAccount": "1776",
      "glName": "VAT payable"
    },
    {
      "account": "tax_payable:EU_OSS_UNION:*",
      "glAccount": "1777",
      "glName": "OSS VAT payable"
    },
    {
      "account": "revenue",
      "glAccount": "8400",
      "glName": "Revenue"
    },
    {
      "account": "processor_fees",
      "glAccount": "4970",
      "glName": "Payment fees"
    }
  ],
  "expectedVersion": 2
}
```

Response (`200`):

```json
{
  "object": "gl_mapping",
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47zm",
  "livemode": false,
  "rules": [
    {
      "account": "processor_clearing",
      "glAccount": "1360",
      "glName": "Stripe clearing"
    },
    {
      "account": "bank",
      "glAccount": "1200",
      "glName": "Bank"
    },
    {
      "account": "tax_payable",
      "glAccount": "1776",
      "glName": "VAT payable"
    },
    {
      "account": "tax_payable:EU_OSS_UNION:*",
      "glAccount": "1777",
      "glName": "OSS VAT payable"
    },
    {
      "account": "revenue",
      "glAccount": "8400",
      "glName": "Revenue"
    },
    {
      "account": "processor_fees",
      "glAccount": "4970",
      "glName": "Payment fees"
    }
  ],
  "version": 3,
  "updatedAt": "2026-10-01T09:00:00.000Z",
  "updatedBy": "usr_01m4tx5nj8emwbyf7y9vcf47zk"
}
```

## Errors

| Code                                                                                             | HTTP status | When                                                                                                                             | What to do                                                                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------------------------------------ | ----------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)           | 400         | A rule is malformed (a field missing or too long), or there are more than 200.                                                   | Fix the fields listed in `details.issues` and send the request again.                                                                                                                                                                                                                                                                 |
| [`gl_mapping_invalid`](https://ownrecord.io/docs/reference/errors.md#gl_mapping_invalid)         | 400         | A rule's account isn't a kind, code or prefix, a glAccount isn't valid, or two rules map the same accounts (`details.problems`). | Fix the rules listed in `details.problems` (`index`, `field`, `message`): `account` is an account kind (e.g. `tax_payable`), an account code, or a code prefix ending in `*` (e.g. `tax_payable:EU_OSS_UNION:*`), each at most once; `glAccount` is 1–40 letters, digits, and `.`, `-`, `_`, `/` or spaces inside; at most 200 rules. |
| [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict)             | 409         | The mapping changed since you read it (`expectedVersion`).                                                                       | Fetch the object again and retry the change.                                                                                                                                                                                                                                                                                          |
| [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused) | 409         | The idempotency key was used before for another request.                                                                         | Use a new Idempotency-Key for a different request, or resend the original request unchanged.                                                                                                                                                                                                                                          |
| [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required)                 | 400         | The request names no legal entity (an org key without `Ownrecord-Entity`, or a brand key: exports belong to the entity).         | Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (`details.required_scope`).                                                                                                                                                                                    |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)           | 403         | You can't export this entity's books or change its mapping (`ledger:write`: finance, owners and admins).                         | Ask an organization owner or admin for a role or scope that allows this action.                                                                                                                                                                                                                                                       |
| [`livemode_not_allowed`](https://ownrecord.io/docs/reference/errors.md#livemode_not_allowed)     | 403         | You have no live-mode access, so you can't use live exports.                                                                     | Use test mode, or credentials for the target's mode. Developers need live access granted by an organization owner or admin.                                                                                                                                                                                                           |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required), [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected), [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired), [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
