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

# Migrate from API v1 to API v2

> Update the routes and contracts of your integration when you move from Midaz v3.x.x to Midaz v4.x.x.

This guide compares the HTTP contracts of Midaz v3.8.6 and Midaz v4.x.x. It covers the Ledger, CRM and Fees. It describes Midaz v4.3.0. The text names the version of each behavior that starts after v4.0.0.

> [!WARNING]
>
> Move your integration to the v2 contracts by April 29, 2027. After this date, Lerian removes the v1 contracts of the Ledger.

> [!NOTE]
>
> Do not replace `/v1` with `/v2` everywhere in your integration. Midaz v3.x.x and Midaz v4.x.x are product versions. v1 and v2 are HTTP contract versions. Midaz v4.x.x serves v1 and v2 contracts side by side, and some v1 routes have no v2 route.

## Ledger routes

***

Replace `{organization_id}`, `{ledger_id}` and the other identifiers in the paths with the values of your integration.

| Task | API v1 | API v2 | Change |
| :- | :- | :- | :- |
| Create a transaction | `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/json` `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/inflow` `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/outflow` | [`POST /v2/transactions/direct`](https://docs.lerian.studio/en/reference/products/midaz/v2/create-transaction-direct) [`POST /v2/transactions/hold`](https://docs.lerian.studio/en/reference/products/midaz/v2/create-transaction-hold) | Move the organization and the ledger from the path to the body. See [Organization and ledger in the transaction body](#organization-and-ledger-in-the-transaction-body). |
| Block and unblock | `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/block` `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/unblock` | [`POST /v2/transactions/block`](https://docs.lerian.studio/en/reference/products/midaz/v2/create-transaction-block) [`POST /v2/transactions/unblock`](https://docs.lerian.studio/en/reference/products/midaz/v2/create-transaction-unblock) | Move the organization and the ledger from the path to the body. See [Organization and ledger in the transaction body](#organization-and-ledger-in-the-transaction-body). |
| Read and update transactions | `GET /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions` `GET` and `PATCH /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}` | [`GET /v2/organizations/{organization_id}/ledgers/{ledger_id}/transactions`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-all-transactions) `GET` and `PATCH /v2/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}` | Same methods. Update how you read the response. See [Responses and errors](#responses-and-errors). |
| Commit, cancel and revert | `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}/commit` `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}/cancel` `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}/revert` | [`POST /v2/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}/commit`](https://docs.lerian.studio/en/reference/products/midaz/v2/commit-transaction) [`POST /v2/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}/cancel`](https://docs.lerian.studio/en/reference/products/midaz/v2/cancel-transaction) [`POST /v2/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}/revert`](https://docs.lerian.studio/en/reference/products/midaz/v2/revert-transaction) | Same methods. Update how you read the response. |
| Update an operation of a transaction | `PATCH /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}/operations/{operation_id}` | [`PATCH /v2/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}/operations/{operation_id}`](https://docs.lerian.studio/en/reference/products/midaz/v2/update-operation) | Change the prefix only. The response does not change. |
| Accounts | `/v1/organizations/{organization_id}/ledgers/{ledger_id}/accounts` and its sub-routes | [`/v2/organizations/{organization_id}/ledgers/{ledger_id}/accounts`](https://docs.lerian.studio/en/reference/products/midaz/v2/list-accounts) and its sub-routes | Review the holder rules. See [Accounts and rules in API v2](#accounts-and-rules-in-api-v2). |
| Ledgers, balances and operations | The v1 routes | The v2 routes with the same methods | The success responses do not change. The error format changes. |
| Transaction routes and operation routes | `/v1/organizations/{organization_id}/ledgers/{ledger_id}/transaction-routes` `/v1/organizations/{organization_id}/ledgers/{ledger_id}/operation-routes` | [`/v2/organizations/{organization_id}/ledgers/{ledger_id}/transaction-routes`](https://docs.lerian.studio/en/reference/products/midaz/v2/list-transaction-routes) [`/v2/organizations/{organization_id}/ledgers/{ledger_id}/operation-routes`](https://docs.lerian.studio/en/reference/products/midaz/v2/list-operation-routes) | Same methods. See the API reference for the v2 response. |

Organizations, assets, segments, portfolios, account types, ledger settings and metadata indexes also have v2 routes. Keep the method and the scope of each route. Not all of them take an organization and a ledger in the path:

* `/v2/organizations` and `/v2/organizations/{organization_id}/ledgers` take the organization only.
* `/v2/settings/metadata-indexes` takes no organization and no ledger.
* `/v2/transactions/direct`, `/v2/transactions/hold`, `/v2/transactions/block` and `/v2/transactions/unblock` take no organization and no ledger in the path.

Midaz v4.x.x serves every v1 route of the Midaz v3.x.x Ledger. The API reference marks all of them as deprecated, and Lerian removes them after April 29, 2027. Until then, they keep the v1 error format.

## Organization and ledger in the transaction body

***

> [!WARNING]
>
> When you move a create, block or unblock request to v2, move the organization and the ledger from the path to the body. The v2 path of these routes has no `{organization_id}` and no `{ledger_id}`.

| API v1: the path carries the organization and the ledger | API v2: the path carries only the action | Where the organization and the ledger go |
| :- | :- | :- |
| `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/json` `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/inflow` `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/outflow` | `POST /v2/transactions/direct` `POST /v2/transactions/hold` | `organizationId` and `ledgerId` in each item of `debits` and `credits` |
| `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/block` | `POST /v2/transactions/block` | `organizationId` and `ledgerId` in each item of `debits` and `credits` |
| `POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/unblock` | `POST /v2/transactions/unblock` | `organizationId` and `ledgerId` in each item of `debits` and `credits` |

The v2 body has no top-level `organizationId` or `ledgerId`. Each item names the organization and the ledger of its own account:

```text theme={null}
# API v1: the organization and the ledger are in the path
POST /v1/organizations/3fa85f64-5717-4562-b3fc-2c963f66afa6/ledgers/9c858901-8a57-4791-81fe-4a34d4dd8ab5/transactions/json

# API v2: the organization and the ledger are in each item of the body
POST /v2/transactions/direct
```

```json theme={null}
{
  "debits": [
    {
      "alias": "@source",
      "organizationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "ledgerId": "9c858901-8a57-4791-81fe-4a34d4dd8ab5",
      "amount": "100.00"
    }
  ]
}
```

This excerpt shows one item only. A complete body also has `asset`, `amount` and `credits`. See the [example of a direct transaction](#example-of-a-direct-transaction).

Follow these rules:

* Send `organizationId` and `ledgerId` in every item. Both are required, and both are UUIDs.
* All items must name the same organization and the same ledger.
* From v4.1.0, `direct` and `hold` accept items in different ledgers when every ledger enables `crossLedger.enabled`. See [cross-Ledger transactions](https://docs.lerian.studio/en/products/midaz/ledgers.md#how-to-orchestrate-cross-ledger-transactions).
* `block` and `unblock` always need one organization and one ledger in all items.

The other transaction routes keep the organization and the ledger in the path: list, read, update, commit, cancel, revert and update an operation. For the full v2 body, see [Transaction contract](#transaction-contract).

## Exceptions

***

These routes need specific handling. Do not apply the tables of this guide to them.

* **Annotations.** [`POST /v1/organizations/{organization_id}/ledgers/{ledger_id}/transactions/annotation`](https://docs.lerian.studio/en/reference/products/midaz/v1/create-transaction-annotation) has no v2 route.
* **Asset rates.** These v1 routes have no v2 route:
  * [`PUT /v1/organizations/{organization_id}/ledgers/{ledger_id}/asset-rates`](https://docs.lerian.studio/en/reference/products/midaz/v1/create-or-update-asset-rate)
  * [`GET /v1/organizations/{organization_id}/ledgers/{ledger_id}/asset-rates/{external_id}`](https://docs.lerian.studio/en/reference/products/midaz/v1/get-asset-rate-by-external-id)
  * [`GET /v1/organizations/{organization_id}/ledgers/{ledger_id}/asset-rates/from/{asset_code}`](https://docs.lerian.studio/en/reference/products/midaz/v1/get-all-asset-rates-by-asset-code)
* **Tracer.** Tracer keeps its own v1 API. See the [Tracer API quick start](https://docs.lerian.studio/en/reference/products/tracer/tracer-api-quick-start.md).

Stop using the annotation and asset rate routes by April 29, 2027. After this date, they are not available, and Midaz has no v2 route to replace them.

## CRM and Fees in API v2

***

In Midaz v4.x.x, the Ledger serves CRM and Fees as modules. Update the base URL of CRM and Fees in your environment. Midaz v4.x.x serves the CRM and Fees routes in v2 only. It has no v1 copy of them.

Each route keeps its method, unless the table says otherwise.

### CRM

The v1 routes below come from CRM in Midaz v3.x.x. CRM in Midaz v3.x.x means the CRM component of Midaz v3.5.0 to v3.8.6 and the standalone CRM plugin. Midaz v4.x.x renames aliases to instruments.

| CRM in Midaz v3.x.x | CRM in Midaz v4.x.x |
| :- | :- |
| `GET` and `POST /v1/holders` | [`GET`](https://docs.lerian.studio/en/reference/products/midaz/v2/list-holders) and [`POST /v2/organizations/{organization_id}/holders`](https://docs.lerian.studio/en/reference/products/midaz/v2/create-holder) |
| `GET`, `PATCH` and `DELETE /v1/holders/{holder_id}` | [`GET`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-holder-by-id), [`PATCH`](https://docs.lerian.studio/en/reference/products/midaz/v2/update-holder) and [`DELETE /v2/organizations/{organization_id}/holders/{holder_id}`](https://docs.lerian.studio/en/reference/products/midaz/v2/delete-holder) |
| `GET /v1/aliases` | [`GET /v2/organizations/{organization_id}/instruments`](https://docs.lerian.studio/en/reference/products/midaz/v2/list-instruments) |
| `POST /v1/holders/{holder_id}/aliases` | [`POST /v2/organizations/{organization_id}/holders/{holder_id}/instruments`](https://docs.lerian.studio/en/reference/products/midaz/v2/create-instrument) |
| `GET`, `PATCH` and `DELETE /v1/holders/{holder_id}/aliases/{alias_id}` | [`GET`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-instrument-by-id), [`PATCH`](https://docs.lerian.studio/en/reference/products/midaz/v2/update-instrument) and [`DELETE /v2/organizations/{organization_id}/holders/{holder_id}/instruments/{instrument_id}`](https://docs.lerian.studio/en/reference/products/midaz/v2/delete-instrument) |
| `DELETE /v1/holders/{holder_id}/aliases/{alias_id}/related-parties/{related_party_id}` | [`DELETE /v2/organizations/{organization_id}/holders/{holder_id}/instruments/{instrument_id}/related-parties/{related_party_id}`](https://docs.lerian.studio/en/reference/products/midaz/v2/delete-related-party) |
| `POST /v1/organizations/{organization_id}/encryption/provision` | [`POST /v2/organizations/{organization_id}/encryption/provision`](https://docs.lerian.studio/en/reference/products/midaz/v2/provision-encryption) |
| `GET /v1/organizations/{organization_id}/encryption/status` | [`GET /v2/organizations/{organization_id}/encryption/status`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-provisioning-status) |
| `GET /v1/organizations/{organization_id}/protection/audit` | [`GET /v2/organizations/{organization_id}/protection/audit`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-audit-events) |

The last four rows apply only to the CRM component of Midaz v3.x.x. The standalone CRM plugin does not have these routes.

### Fees

The v1 routes below come from the standalone Fees plugin. Midaz v3.x.x has no Fees component.

| Fees plugin | Fees in Midaz v4.x.x |
| :- | :- |
| `GET` and `POST /v1/packages` | [`GET`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-all-packages) and [`POST /v2/organizations/{organization_id}/ledgers/{ledger_id}/packages`](https://docs.lerian.studio/en/reference/products/midaz/v2/create-package) |
| `GET`, `PATCH` and `DELETE /v1/packages/{package_id}` | [`GET`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-package-by-id), [`PATCH`](https://docs.lerian.studio/en/reference/products/midaz/v2/update-package) and [`DELETE /v2/organizations/{organization_id}/ledgers/{ledger_id}/packages/{package_id}`](https://docs.lerian.studio/en/reference/products/midaz/v2/delete-package) |
| `POST /v1/estimates` | [`POST /v2/organizations/{organization_id}/ledgers/{ledger_id}/estimates`](https://docs.lerian.studio/en/reference/products/midaz/v2/estimate-fee-calculation) |
| `GET` and `POST /v1/billing-packages` | [`GET`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-all-billing-packages) and [`POST /v2/organizations/{organization_id}/ledgers/{ledger_id}/billing-packages`](https://docs.lerian.studio/en/reference/products/midaz/v2/create-billing-package) |
| `GET`, `PATCH` and `DELETE /v1/billing-packages/{billing_package_id}` | [`GET`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-billing-package-by-id), [`PATCH`](https://docs.lerian.studio/en/reference/products/midaz/v2/update-billing-package) and [`DELETE /v2/organizations/{organization_id}/ledgers/{ledger_id}/billing-packages/{billing_package_id}`](https://docs.lerian.studio/en/reference/products/midaz/v2/delete-billing-package) |
| `POST /v1/billing/calculate` | [`POST /v2/organizations/{organization_id}/ledgers/{ledger_id}/billing/calculate`](https://docs.lerian.studio/en/reference/products/midaz/v2/calculate-billing) |
| `POST /v1/fees` | No HTTP route. Midaz applies fees inside the v2 transaction flow. |

## Scope and permission changes

***

* Send the organization in the path. Do not send the `X-Organization-Id` header.
* In Fees, send the ledger in the path. Remove `ledgerId` from the body of packages, estimates, billing packages and billing calculation.
* The package and billing package lists always cover one ledger. In the plugin, `ledgerId` was an optional query parameter. To list the packages of a whole organization, call the list once for each ledger.
* In CRM instruments, keep `ledgerId` and `accountId` in the body.
* Use estimates only to simulate fees. An estimate does not record anything.

Midaz v4.x.x checks permissions in the `midaz` group. Grant the new permissions in [Access Manager](https://docs.lerian.studio/en/platform/access-manager.md) before you move traffic. Do not assume that the old grants still apply.

| Permission in Midaz v3.x.x | Permission in Midaz v4.x.x |
| :- | :- |
| `plugin-crm:holders` | `midaz:holders` |
| `plugin-crm:aliases` | `midaz:instruments` |
| `plugin-crm:encryption` | `midaz:encryption` |
| `plugin-crm:protection` | `midaz:protection` |
| `plugin-fees:packages` | `midaz:packages` |
| `plugin-fees:estimates` | `midaz:estimates` |
| `plugin-fees:billing-packages` | `midaz:billing-packages` |
| `plugin-fees:billing-calculate` | `midaz:billing-calculate` |
| `plugin-fees:fees` | None. The route does not exist in Midaz v4.x.x. |

New routes do not move your data. Plan the migration of the CRM and Fees data, relationships and identifiers as a separate task. For the history of standalone CRM, see [CRM migration history](https://docs.lerian.studio/en/products/midaz/crm/crm-migration-guide.md). To move the fee packages of Bank Transfer, see [Migrate Bank Transfer fees to Midaz](https://docs.lerian.studio/en/products/midaz/fees/fees-migration-guide.md).

## Transaction contract

***

The v2 request body has these fields:

* `asset`: the asset code.
* `amount`: the total, as a decimal string.
* `debits` and `credits`: arrays with one or more items each.

Each item of `debits` and `credits` has these fields:

* `alias`: the account alias.
* `organizationId` and `ledgerId`: the UUIDs of the organization and the ledger of the account. See [Organization and ledger in the transaction body](#organization-and-ledger-in-the-transaction-body).
* `amount` or `share`: send exactly one of the two.

Select the intent with the route. The v2 body has no `pending` field:

* Use `/v2/transactions/direct` to post the transaction at once.
* Use `/v2/transactions/hold` to create a pending transaction. Then commit or cancel it.

When you convert an inflow or an outflow, write both sides. On the side that v1 left out, use `@external/` followed by the asset code in upper case, for example `@external/BRL`.

### Example of a direct transaction

`POST /v2/transactions/direct`. Replace the aliases and the UUIDs with the real values.

```json theme={null}
{
  "asset": "BRL",
  "amount": "100.00",
  "debits": [
    {
      "alias": "@source",
      "organizationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "ledgerId": "9c858901-8a57-4791-81fe-4a34d4dd8ab5",
      "amount": "100.00"
    }
  ],
  "credits": [
    {
      "alias": "@destination",
      "organizationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "ledgerId": "9c858901-8a57-4791-81fe-4a34d4dd8ab5",
      "amount": "100.00"
    }
  ]
}
```

### Fields that the v2 body does not accept

Midaz refuses a v2 body that has an unknown field, with a `400` response. Review your flow if it sends any of these v1 fields:

* `rate` and `remaining` in an item.
* `metadata` in an item. The transaction itself still accepts `metadata`.
* `transactionDate`.
* `balanceKey` in an item, on Midaz v4.0.x only. From v4.1.0, each item accepts an optional `balanceKey`.

Do not copy these fields into the v2 body. Find a supported alternative first.

## Responses and errors

***

### Transaction responses

* `source` and `destination` become `debit` and `credit`, in the singular.
* The transaction no longer has `route` and `chartOfAccountsGroupName`.
* The operations inside a transaction no longer have `route` and `chartOfAccounts`. They keep `routeId`, `routeCode` and `routeDescription`.
* The responses of an operation that you read alone do not change.
* From v4.1.0, a `direct` or `hold` that spans ledgers returns a group with `groupId` and `transactions[]`. Commit, cancel and revert can also return a group.

The v2 transaction also has fields that v1 does not have. See [`GET /v2/organizations/{organization_id}/ledgers/{ledger_id}/transactions/{transaction_id}`](https://docs.lerian.studio/en/reference/products/midaz/v2/get-transaction) in the API reference.

### Errors

The v2 routes return errors in the RFC 9457 format, with `Content-Type: application/problem+json`. Read these members:

* `type`, `title`, `status` and `detail`.
* `code`: the Midaz error code. The same error has the same code in v1 and v2.
* `errors[]`: the field errors, when the request has them.

For example, a `POST /v2/transactions/direct` request that still sends the v1 field `transactionDate` gets this response:

```http theme={null}
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
```

```json theme={null}
{
  "type": "https://errors.lerian.studio/v1/0053",
  "title": "Unexpected Fields in the Request",
  "status": 400,
  "detail": "The request body contains more fields than expected. Please send only the allowed fields as per the documentation. The unexpected fields are listed in the fields object.",
  "errors": [
    {
      "message": "unexpected field",
      "location": "transactionDate"
    }
  ],
  "code": "0053"
}
```

The `type` URI ends with the error code. Its `/v1` segment is the version of the error catalog, not the version of the API.

Do not rely on `message` or `fields`. They belong to the v1 error format. In v2, only `413` and `504` responses also carry a top-level `message`. See [Error handling](https://docs.lerian.studio/en/reference/error-handling.md) and the [Midaz error list](https://docs.lerian.studio/en/reference/products/midaz/error-list.md).

## Accounts and rules in API v2

***

### Account routes

The v2 account routes let you do these tasks:

* Read an account by alias and by external code.
* Update and delete an account.
* Count the accounts with `HEAD`.

Send `holderId` when you create an account. Read `holderId` and `holderCheckSkipped` in the v2 account responses.

### Holder rules

* When `accounting.requireHolder` is on, each account needs a holder of the organization. Without a holder, Midaz does not create the account.
* To skip that check, send `skip.holder`. Midaz accepts it only when `overrides.allowHolderSkip` is on. Otherwise, Midaz refuses the request with `422` and error `0490`.
* When `accounting.requireHolder` is off and you send no holder, a non-external account gets the holder of its organization. An external account gets no holder.
* When `accounting.requireHolder` is off, Midaz does not check that the `holderId` you send exists.
* An account that you create through v1 gets no holder. v1 does not apply these rules.

See [Ledger settings](https://docs.lerian.studio/en/products/midaz/ledgers.md#ledger-settings).

### Fees and Tracer

A v2 transaction runs two steps that v1 does not run:

* **Fees.** Midaz charges a fee when a fee package matches the transaction.
* **Tracer.** Midaz reserves the transaction in Tracer when the Tracer mode of the ledger is not `off`.

From v4.1.0, a transaction can skip these steps with `skip.fees` or `skip.tracer`. The ledger must turn on `overrides.allowFeeSkip` or `overrides.allowTracerSkip` first.

Validate your fee packages and your Tracer limits before you move traffic. After the migration, you can see new charges and new refusals. See [Midaz and plugin compatibility](https://docs.lerian.studio/en/products/midaz/midaz-version-compatibility.md).

## Migration checklist

***

* [ ] List the routes that you use, the product version and the base URL of each service. Separate the routes with a v2 route from the routes that have none. Plan to stop using the routes that have none by April 29, 2027.
* [ ] For each transaction, select `direct` or `hold`. Update `debits`, `credits`, the decimal values and the scope of each item.
* [ ] Change CRM aliases to instruments. Move the organization and the ledger of Fees to the path. After the data migration, check the relationships and the identifiers.
* [ ] Grant the `midaz` permissions. Test the holder rules of the accounts in a staging environment.
* [ ] Update how you read success and error responses. Test removed fields, invalid input, missing resources and denied access.
* [ ] Test each flow that your integration uses: create, read, hold, commit, cancel, revert, block and unblock. Check balances, operations, fees and refusals by Tracer limits.
* [ ] Keep sending `X-Idempotency` and `X-TTL` on create requests. Do not send the same transaction through v1 and v2 to compare results. Use controlled data and scenarios in a staging environment.
* [ ] Move traffic in controlled steps. Monitor errors and financial results. Keep a rollback plan that does not send processed transactions again.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.