> ## 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.

# Partners in Midaz

> Confine a partner's Midaz credential to one organization and, inside it, to the ledgers, accounts, aliases, portfolios, segments, holders, or routes you choose, and read the refusals it receives.

A [partner](https://docs.lerian.studio/en/platform/access-manager/features/partners/overview) can call Midaz with its own credential. In Midaz, you confine that credential to one organization and, if you want, to some items inside it. This page shows how to choose that scope, what each choice reaches, and what the partner receives when it goes outside it.

Every Midaz partner needs a scope. The organization is always required.

## Step by step

***

**1. Choose where the partner acts**

Pick the narrowest scope that covers what the partner needs. Each row is a restriction you can add. The **API field** is the `field` of a scope entry; the Console shows the name in the first column.

| Restriction | API field | What the partner reaches | What it is refused |
| - | - | - | - |
| **Midaz organization** (required, one value) | `organizationId` | With no other restriction: everything its permissions give inside that organization, in any ledger. | Every other organization. |
| **Ledger** | `ledgerId` | Its own ledgers by ID, and the accounts, balances, and transactions inside them. It also reads its organization. | Other ledgers, the list of the organization's ledgers, holders, instruments without a ledger, the CRM audit records, and operation and transaction routes (see the last row). |
| **Account** | `accountId` | Each listed account, its balances, and its operations, by account ID. | Listing the accounts of the ledger, a balance by its own ID, transactions (list and by ID), and the routes addressed by alias. |
| **Account alias** | `accountAlias` | Each listed account and its balances, through the routes addressed by alias. It can create transactions from its own aliases. | The routes addressed by account ID. |
| **Portfolio** or **Segment** | `portfolioId`, `segmentId` | Listing accounts filtered by its own portfolio or segment, and creating accounts in it. | An account by ID, balances, and transactions. |
| **Holder** | `holderId` | Its own holders, their accounts, and the account list filtered by its own holder. | Listing all holders. |
| **Operation route** or **Transaction route** | `operationRouteId`, `transactionRouteId` | Only the routes whose IDs you list, read by ID under the partner's ledger. | Listing routes, and reading a route under the organization instead of the ledger when the partner is also restricted by ledger. |

The scope catalog lists every Midaz restriction, including the ones not in this table. Read it with [Retrieve a product's scope catalog](https://docs.lerian.studio/en/reference/platform/access-manager/retrieve-a-product-s-scope-catalog.md) for `midaz`.

Some rules apply to every scope:

* A request that does not name an item of a restricted type is refused when it acts on what that restriction confines. For example, a partner restricted to some ledgers cannot list all the ledgers of its organization. A request outside what the restriction confines only needs the organization.
* A request that lists several values, such as several aliases in a body or a query, is refused when any one of them is outside the scope.
* Routes that act on the whole tenant, such as listing organizations or reading tenant settings, are refused to every partner.

**2. Give the partner its permissions and scope**

Create the partner, or change it, in the [Console](https://docs.lerian.studio/en/platform/access-manager/features/partners/console.md) or with the [Identity API](https://docs.lerian.studio/en/platform/access-manager/features/partners/api.md). The product slug is `midaz`.

* Add the scope entry for the organization, with exactly one value. Without it, Access Manager refuses the partner with `400` and `IDE-0001`. With two organizations, it refuses with `IDE-0002`.
* Add the other restrictions you chose in step 1.
* Check the resources and actions. The most you can give is what the `midaz-editor-role` role holds in your tenant. Above that, Access Manager refuses with `IDE-1043`.

Access Manager checks each write you give against the scope:

* A write that acts on the whole tenant, such as creating an organization, is never given to a partner.
* A write that acts above the partner's scope is refused. For example, a partner restricted to one ledger cannot receive the right to create, change, or delete ledgers, holders, or operation and transaction routes: those writes act on the whole organization.
* Both refusals answer `400` with `IDE-1056`. The Console marks the line **Outside the scope**.

To give read access to operation or transaction routes, list the route IDs in the scope. Without them, Access Manager refuses the partner with `400` and `IDE-1052`. Access Manager does not check that a route ID belongs to the partner's ledger: the partner reads every route whose ID you list. List only the routes of its own ledgers.

Do not give `transactions` with `post` to a partner restricted only by portfolio, segment, or holder. Access Manager accepts it, but every transaction of that partner is refused.

**3. Create its Midaz application**

Create the partner's application with the product `midaz`. See [Create the partner's applications](https://docs.lerian.studio/en/platform/access-manager/features/partners/api.md#create-the-partners-applications) in the API guide, or [in the Console](https://docs.lerian.studio/en/platform/access-manager/features/partners/console.md#create-the-partners-applications). Send the client ID and the client secret to the partner through a secure channel.

**4. Tell the partner how to name its items**

In most Midaz routes, the organization and the ledger are already in the path, so the partner sends nothing extra. The partner names its other items in the paths, queries, and bodies that Midaz already has. Every value must be inside the partner's scope.

In the table, `…/ledgers/{ledger_id}` stands for `/organizations/{organization_id}/ledgers/{ledger_id}`, after `/v1` or `/v2`.

| Routes | Writes | Reads and routes by ID |
| - | - | - |
| Organizations and ledgers: `/v1` and `/v2` `/organizations/{organization_id}`, `/organizations/{organization_id}/ledgers` | Path `organization_id` to create a ledger, and `ledger_id` to change it, delete it, or change its settings | Path `organization_id`, and `ledger_id` for one ledger and its settings |
| Accounts: `/v1` and `/v2` `…/ledgers/{ledger_id}/accounts` | To create: body `portfolioId`, `segmentId`, and `parentAccountId`, and on `/v2` also `holderId`. To change, delete, or close (`/v2` only): path `account_id` | To list: query `portfolio_id`, `segment_id`, or `holder_id`. One account: path `account_id`, or `alias` on `…/accounts/alias/{alias}` |
| Accounts of a holder: `/v2/organizations/{organization_id}/holders/{holder_id}/accounts`, `/v2/…/ledgers/{ledger_id}/holders/{holder_id}/accounts` | To create an account for the holder: path `holder_id`, and body `portfolioId`, `segmentId`, and `parentAccountId` | Path `holder_id`, and query `ledger_id`. The answer holds only that ledger's accounts |
| Account block exceptions: `/v2/…/ledgers/{ledger_id}/accounts/block-exceptions` | Body `exceptions[].accountAlias` | None: the route only writes |
| Balances: `/v1` and `/v2` `…/ledgers/{ledger_id}/balances`, `…/accounts/{account_id}/balances`, `…/accounts/alias/{alias}/balances` | To create: path `account_id`. To change or delete: path `balance_id` | Path `account_id` or `alias` for the balances of an account, and `balance_id` for one balance and its history |
| Operations: `/v1` and `/v2` `…/accounts/{account_id}/operations`, `…/transactions/{transaction_id}/operations/{operation_id}` | To change: path `transaction_id` and `operation_id` | Path `account_id`, and `operation_id` for one operation |
| Transactions on `/v1`: `/v1/…/ledgers/{ledger_id}/transactions` | To create with `json`, `outflow`, `annotation`, `block`, or `unblock`: body `send.source.from[].accountAlias`. `inflow` names only the ledger. To commit, cancel, revert, or change: path `transaction_id` | Path `transaction_id` |
| Transactions on `/v2`: `/v2/transactions`, `/v2/…/ledgers/{ledger_id}/transactions` | To create with `direct`, `hold`, `block`, or `unblock`: body `debits[].organizationId`, `debits[].ledgerId`, and `debits[].alias`, and the same three in `credits[]`. With `batch`, the same fields in each item of `transactions[]`. To commit, cancel, revert, or change: path `transaction_id` | Path `transaction_id` |
| Holders and instruments: `/v2/organizations/{organization_id}/holders`, `/v2/organizations/{organization_id}/holders/{holder_id}/instruments`, `/v2/organizations/{organization_id}/instruments` | To change or delete a holder: path `holder_id`. To create an instrument: path `holder_id`, and body `ledgerId` and `accountId`, always. To change or delete one: path `holder_id` and `instrument_id` | Path `holder_id`, and `instrument_id` for one instrument. To list instruments: query `holder_id`, `account_id`, or `ledger_id` |
| Operation and transaction routes: `/v1` and `/v2` `…/ledgers/{ledger_id}/operation-routes`, `…/ledgers/{ledger_id}/transaction-routes`, and on `/v2` also under `/organizations/{organization_id}` | Path `organization_id`, and `ledger_id` on the routes under the ledger | Path `ledger_id`, with `operation_route_id` or `transaction_route_id`. The routes under the organization do not name the ledger |
| Fees and billing: `/v2/…/ledgers/{ledger_id}/packages`, `…/billing-packages`, `…/fee-debts` | Fee package: body `segmentId`. Billing package: body `accountTarget.segmentId`, `accountTarget.portfolioId`, and `accountTarget.aliases[]`. To collect fee debts: body `accountAlias` | Fee packages: query `segmentId`. Fee debts: query `account_alias`. One item: path `package_id`, `billing_package_id`, or `fee_debt_id` |
| Ledger setup and dashboard: assets, asset rates, account types, portfolios, segments, and `…/ledgers/{ledger_id}/dashboard` | Path `ledger_id`. To change or delete one item: path `asset_id`, `account_type_id`, `portfolio_id`, or `segment_id` | Path `ledger_id`. One item: path `asset_id`, `account_type_id`, `portfolio_id`, or `segment_id` |

> [!WARNING]
>
> On the `/v1` transaction routes, only the source accounts are checked. A partner restricted by alias can send money from its own aliases to any account of its ledger. To keep it between its own accounts, do not give it `transactions` with `post`.

On the `/v2/transactions` routes, every debit and every credit names an organization and a ledger inside the scope. A partner restricted by alias can use only its own aliases on both sides.

A partner restricted by account or alias cannot use these transaction routes: `inflow` on `/v1`, and commit, cancel, revert, change, read by ID, or list. A partner restricted by alias can create a hold. A partner restricted by account ID cannot create transactions. To let it move money from specific accounts, restrict it by alias.

A body field that takes one item takes one value, such as `portfolioId` or the `alias` of a debit. Do not send a list in it.

**5. Confirm that it works**

This check assumes the partner has the `accounts` resource with the `get` action on Midaz, and a scope restricted to one organization and one or more of its ledgers. Use the partner's credential. List the accounts of a ledger inside its scope. The answer is `200`:

```bash theme={null}
MIDAZ_BASE_URL="https://midaz.<your-domain>"              # your Midaz ledger API base URL
MIDAZ_BEARER_TOKEN="paste-the-partner-access-token-here"
ORGANIZATION_ID="paste-the-organization-id-here"          # the organization in the partner's scope
LEDGER_ID="paste-a-ledger-id-in-the-scope-here"

curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Authorization: Bearer ${MIDAZ_BEARER_TOKEN}" \
  "${MIDAZ_BASE_URL}/v1/organizations/${ORGANIZATION_ID}/ledgers/${LEDGER_ID}/accounts"
```

Send the same request with a ledger of the same organization that is outside the scope. The answer is `403` with code `AUT-1020`.

## If something fails

***

| Code | Status | What it means | What to do |
| - | - | - | - |
| `AUT-1020` | 403 | The request is outside the partner's permissions or outside its scope, or it does not name an item the scope requires. The message names the resource and the action: "the credential is not allowed to perform `<resource>:<action>` on this request". | Check that the request names its organization and its items, and that they are in the partner's scope. Check also that the partner has the permission for that route. |
| `0047` | 400 | Midaz cannot read the scope from the request: a field it needs is missing, empty, or not a string. | Send every field the scope needs, as a non-empty string. |
| `AUT-1009` | 401 | The partner is suspended. | Reactivate the partner. |
| `AUT-1010` | 401 | The request is outside the partner's validity window. | Change the validity window. |
| `AUT-0021` | 403 | The request comes from an address outside the partner's IP list, or your tenant's list when the partner has none. | Add the address to the partner's IP list. |

The answer does not say whether the permissions or the scope refused the request. This is on purpose: a partner cannot use a refusal to learn which items exist outside its scope.

For every other code, see the [Midaz error list](https://docs.lerian.studio/en/reference/products/midaz/error-list.md). For the refusals when you create or change a partner, see [Error codes](https://docs.lerian.studio/en/platform/access-manager/features/partners/api.md#error-codes) in the API guide.


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