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

# Manage partners via API

> Create, read, change, and delete partners with the Identity API, issue their credentials, and handle the error codes the operations return.

<Warning>
  This feature is available only in Staging for testing and is not yet available in Production.
</Warning>

The Identity API manages [partners](/en/platform/access-manager/features/partners/overview) and their credentials. Use it when you manage partners from your own tooling instead of the Console.

## The operations

***

| Operation | What it does | Reference |
| - | - | - |
| `GET /v1/partners` | Lists your partners, one page at a time, with `applicationsCount` for each one. | [List partners](/en/reference/platform/access-manager/list-partners) |
| `POST /v1/partners` | Creates a partner. It returns `201` with the new partner and its `id`. No credential is created. | [Create a partner](/en/reference/platform/access-manager/create-a-partner) |
| `GET /v1/partners/{id}` | Returns one partner in full. | [Retrieve a partner](/en/reference/platform/access-manager/retrieve-a-partner) |
| `PATCH /v1/partners/{id}` | Changes a partner: its access, its validity window, its IP allowlist, or its state. | [Update a partner](/en/reference/platform/access-manager/update-a-partner) |
| `DELETE /v1/partners/{id}` | Deletes a partner without applications. It returns `204`. | [Delete a partner](/en/reference/platform/access-manager/delete-a-partner) |
| `GET /v1/partners/ceiling` | Returns the most you can give a partner in one product. | — |

Every operation needs a bearer token with the `partners` permission. Access Manager resolves your tenant from the token. There is no tenant field in the path or in the body, and you never see the partners of another tenant.

To learn which restrictions a product accepts, read its scope catalog with `GET /v1/scope-catalog/{product}`.

## The partner fields

***

| Field | Required on create | Meaning |
| - | - | - |
| `displayName` | Yes | The partner's name, 1 to 128 characters, unique in your tenant. |
| `permissions` | Yes | One or more entries per product, each with its own `product`, `resources`, and `actions`. Each entry needs at least one resource and one action. |
| `scope` | No | One entry per restriction: `product`, `field`, and `values`. `field` is a dimension from the product's scope catalog, such as `organizationId` or `ledgerId`. |
| `ipAllowlist` | No | The partner's own IP allowlist. Each entry has a `cidr` and an optional `description`. |
| `validFrom` | No | The start of the validity window, as an RFC 3339 instant. Absent means "valid now". |
| `validUntil` | No | The end of the validity window, as an RFC 3339 instant. Absent means "no end date". |
| `state` | No | `active` or `suspended`. Every new partner is `active`. You change it with `PATCH`. |

The response also carries `id`, `applicationsCount`, `createdAt`, and `updatedAt`.

Rules that apply to the fields:

* `product` is a product slug from [List Available Applications](/en/reference/platform/access-manager/list-available-applications), such as `midaz`.
* `actions` are lowercase HTTP verbs: `get`, `post`, `put`, `patch`, `delete`. `head` comes with `get`.
* The wildcard `*` is not accepted in `resources` or `actions`. List the values.
* A product in `scope` must also be in `permissions`.
* Midaz requires one `organizationId` for each partner with Midaz permissions. A dimension that the catalog does not mark as multi-valued takes one value only.
* Access Manager does not check that the `values` exist in the product. Use the IDs that the product's own API returns.
* `state` never reads `expired`. After `validUntil`, the partner still reads `active`, with a `validUntil` in the past.

## The IP allowlist field

***

`ipAllowlist` has three meanings on `PATCH`, and they are not the same:

| You send | What happens |
| - | - |
| The field omitted | The stored list stays as it is. |
| `null` | The partner's own list is deleted. The partner uses your tenant's list again. |
| A list of entries | The partner's own list is replaced by these entries. |
| `[]` | Refused with `IDE-1048`. To block a partner completely, suspend it. |

On `POST`, omit the field or send `null` to use your tenant's list. A partner's own list replaces your tenant's list. It does not add to it.

`validFrom` and `validUntil` work in a similar way on `PATCH`: omit a bound to keep it, send an instant to set it, or send `null` to remove it.

## Examples

***

Replace the placeholders with your Identity API base URL, a bearer token that holds the `partners` permission, and IDs from your own Midaz organization.

<CodeGroup>
  ```bash Create a partner theme={null}
  IDENTITY_BASE_URL="https://identity.<your-domain>"   # your Identity API base URL
  IDENTITY_BEARER_TOKEN="paste-your-access-token-here"

  curl -sS -X POST "${IDENTITY_BASE_URL}/v1/partners" \
    -H "Authorization: Bearer ${IDENTITY_BEARER_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{
      "displayName": "Partner A",
      "permissions": [
        { "product": "midaz", "resources": ["accounts"], "actions": ["get", "post", "patch"] },
        { "product": "midaz", "resources": ["transactions"], "actions": ["get", "post"] }
      ],
      "scope": [
        { "product": "midaz", "field": "organizationId", "values": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"] },
        { "product": "midaz", "field": "ledgerId", "values": ["9c858901-8a57-4791-81fe-4a34d4dd8ab5"] }
      ],
      "ipAllowlist": [
        { "cidr": "203.0.113.10", "description": "Partner A gateway" }
      ]
    }'
  ```

  ```bash Create its application theme={null}
  IDENTITY_BASE_URL="https://identity.<your-domain>"   # your Identity API base URL
  IDENTITY_BEARER_TOKEN="paste-your-access-token-here"
  PARTNER_ID="paste-the-partner-id-here"

  curl -sS -X POST "${IDENTITY_BASE_URL}/v1/applications" \
    -H "Authorization: Bearer ${IDENTITY_BEARER_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "midaz",
      "description": "Partner A access to Midaz",
      "partnerId": "'"${PARTNER_ID}"'"
    }'
  ```

  ```bash Suspend it theme={null}
  IDENTITY_BASE_URL="https://identity.<your-domain>"   # your Identity API base URL
  IDENTITY_BEARER_TOKEN="paste-your-access-token-here"
  PARTNER_ID="paste-the-partner-id-here"

  curl -sS -X PATCH "${IDENTITY_BASE_URL}/v1/partners/${PARTNER_ID}" \
    -H "Authorization: Bearer ${IDENTITY_BEARER_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{ "state": "suspended" }'
  ```

  ```bash Go back to the tenant's IP list theme={null}
  IDENTITY_BASE_URL="https://identity.<your-domain>"   # your Identity API base URL
  IDENTITY_BEARER_TOKEN="paste-your-access-token-here"
  PARTNER_ID="paste-the-partner-id-here"

  curl -sS -X PATCH "${IDENTITY_BASE_URL}/v1/partners/${PARTNER_ID}" \
    -H "Authorization: Bearer ${IDENTITY_BEARER_TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{ "ipAllowlist": null }'
  ```
</CodeGroup>

## Issue the partner's credentials

***

A partner without an application cannot call anything. After you create the partner:

1. Create an application with [Create an Application](/en/reference/platform/access-manager/create-an-application), and send the partner's `id` in `partnerId`. Set `name` to the product slug, such as `midaz`. Create one application per product.
2. Copy `clientId` and `clientSecret` from the response. The response is the only time the secret is shown.
3. Send both values to the partner through a secure channel.

To list the applications of one partner, send its `id` in the `partnerId` query parameter of [List Applications](/en/reference/platform/access-manager/list-applications). If `partnerId` does not name a partner of your tenant, both operations return `404` with `IDE-1046`, and nothing is created.

## Change, suspend, or delete a partner

***

* On `PATCH`, send only the fields you change. A `permissions` or a `scope` list replaces the stored list as a whole. Read the partner first, then send the complete new list.
* To suspend a partner, send `"state": "suspended"`. To reactivate it, send `"state": "active"`.
* A change applies from the partner's next request. A suspension also refuses tokens that the partner already holds.
* You cannot delete a partner that still has applications. The response is `409` with `IDE-1049`, and its `errors` list names each blocking application with its client ID. Delete those applications first.

## Error codes

***

Errors in the partner operations:

| Code | Status | Title | When |
| - | - | - | - |
| `IDE-0001` | 400 | Missing Fields in Request | A required field is missing, or the scope has no entry for a dimension the product requires. |
| `IDE-0002` | 400 | Invalid Field Type in Request | Several values on a single-valued dimension, or a `state` other than `active` or `suspended`. |
| `IDE-0036` | 400 | Invalid IP Allowlist Entry | An `ipAllowlist` entry is not a valid address or CIDR range. |
| `IDE-1040` | 409 | Partner Display Name Already Exists | Another partner of your tenant has the same `displayName`. |
| `IDE-1042` | 400 | Unknown Scope Field | A scope `field` is not in the product's scope catalog. |
| `IDE-1043` | 400 | Permission Above Product Ceiling | A permission is above what the product's editor role holds in your tenant. |
| `IDE-1044` | 400 | Scope Without Permissions | A product is in `scope` but not in `permissions`. |
| `IDE-1045` | 400 | Invalid Validity Window | `validUntil` is not later than `validFrom`. |
| `IDE-1046` | 404 | Partner Not Found | No partner with this `id` exists in your tenant. |
| `IDE-1047` | 400 | Wildcard Not Allowed | `*` is in `resources` or `actions`. |
| `IDE-1048` | 400 | Empty IP Allowlist | `ipAllowlist` is an empty array. |
| `IDE-1049` | 409 | Partner Has Applications | The partner still has applications, so it cannot be deleted. |
| `IDE-1050` | 400 | Duplicate IP Allowlist Entry | The same network appears twice in `ipAllowlist`. |
| `IDE-1054` | 400 | Product Not Ready For Partners | The product has not published its scope catalog yet. |
| `IDE-1055` | 400 | Product Not Opted In To Partners | The product has a scope catalog but does not accept partners. |
| `IDE-1056` | 400 | Partner Write Exceeds Its Scope | A write acts at a level wider than the partner's scope, such as creating ledgers for a partner restricted to one ledger. |

Errors a partner's own system receives:

| Code | Status | When |
| - | - | - |
| `AUT-0021` | 403 | The request comes from an address outside the partner's IP allowlist. |
| `AUT-1009` | 401 | The partner is suspended. Returned on requests to products and on new token requests. |
| `AUT-1010` | 401 | The partner is outside its validity window. Returned on requests to products and on new token requests. |
| None | 403 | The permissions or the scope do not allow the request. The product does not say which one. |

For every other code, see the [Access Manager error list](/en/reference/platform/access-manager/access-manager-error-list).


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