# Команда и права

В одной компании может работать несколько человек и сервисов. У каждого
участника свой вход в кабинет, свой [API-ключ](/guides/authentication) и
свой набор прав. Права определяют, какие эндпоинты доступны ключу и
сессии участника.

## Роли

| Роль | Что значит |
|  --- | --- |
| `owner` | Владелец. Полный доступ ко всему, независимо от списка прав |
| `admin` | Администратор. Доступ — по списку прав |
| `member` | Участник. Доступ — по списку прав |


Роль `admin` или `member` сама по себе прав не даёт: решает список
`permissions`. Назначить владельца через API нельзя — только `admin` или
`member`.

## Права

Право нужно для вызова эндпоинта. Если его нет, API отвечает
`403 PERMISSION_DENIED`, а в поле `required` называет недостающее право.

| Право | Что открывает |
|  --- | --- |
| `cards.read` | `GET /cards`, `GET /cards/{id}`, `GET /cards/balances`, `GET /cards/{id}/balance`, `GET /cards/requests/{id}`, операции и выгрузки (`/cards/transactions`, `/cards/{id}/transactions`, `.csv`), пополнения (`/cards/recharges`, `/cards/{id}/recharges`) |
| `cards.create` | `POST /cards/request`, `POST /cards/requests/{id}/cancel` |
| `cards.recharge` | `POST /cards/recharge/quote`, `POST /cards/recharge` |
| `cards.retire` | `POST /cards/{id}/retire` |
| `cards.sensitive` | `GET /cards/{id}/sensitive`, `GET /cards/{id}/otp/latest` |
| `orders.read` | `GET /orders`, `GET /orders/{id}`, `GET /disputes`, `GET /rates/current` |
| `orders.create` | `POST /orders/prepare`, `POST /orders/accept/{id}` |
| `orders.write` | `POST /orders/{id}/disputes` |
| `balance.read` | `GET /wallets`, `GET /wallets/balance`, `GET /wallets/deposits`, история баланса, `GET /user-wallets`, `GET /user-wallets/blocked`, `GET /user-wallets/deposits`, статус вывода |
| `balance.withdraw` | `POST /user-wallets`, блокировка, замена и вывод с кошельков пользователей — по сути полный контроль над средствами на этих кошельках |
| `payouts.read` | `GET /payouts/config`, `GET /payouts/banks`, `POST /payouts/quote`, `GET /payouts`, `GET /payouts/{id}` |
| `payouts.create` | `POST /payouts` |
| `payouts.write` | `POST /payouts/{id}/cancel`, `POST /payouts/{id}/dispute` |
| `members.read` | `GET /members`, `GET /members/invites` |
| `members.invite` | `POST /members/invites`, `DELETE /members/invites/{id}` |
| `members.remove` | `DELETE /members/{userId}` |
| `members.role.write` | `PATCH /members/{userId}` |
| `settings.write` | `PATCH /auth/company/profile` |
| `apikey.regenerate` | `POST /auth/company/regenerate-key`, `POST /auth/company/api-key/reveal` |
| `webhook.manage` | Секрет вебхуков (`/auth/company/webhook-secret/*`), `POST /webhooks/test`, `GET /webhooks/deliveries` |


`GET /auth/company/profile` доступен любому участнику без отдельного права.

Помимо прав, эндпоинты карт работают только при подключённой программе
карт (иначе `403 CARDS_NOT_ENABLED`), а создание выплат — при подключённых
выплатах (`403 PAYOUTS_NOT_ENABLED`).

:::warning Права, равные доступу владельца

- `members.role.write` позволяет менять роль и права любого участника, кроме
владельца, — в том числе собственные.
- `members.invite` позволяет пригласить участника с любым набором прав.
- Список прав не сверяется с каталогом: значение `*` даёт полный доступ.
- `settings.write` меняет адрес вебхуков, их секрет и адрес, на который
пересылаются депозиты пользователей.


Выдавайте эти права только тем, кому доверили бы роль владельца.

:::

## Приглашение участника

Пригласить можно участника с правом `members.invite`:

```bash
curl -X POST 'https://b2b.lumowallet.io/members/invites' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "accountant@acme.com",
    "role": "member",
    "permissions": ["orders.read", "balance.read"]
  }'
```

```json
{
  "id": "5b1c0e7a-2d3f-4e8a-9b6c-1a2b3c4d5e6f",
  "token": "9f2c…e41a",
  "email": "accountant@acme.com",
  "role": "member",
  "permissions": ["orders.read", "balance.read"],
  "expiresAt": "2026-10-10T12:00:00.000Z",
  "createdAt": "2026-10-03T12:00:00.000Z",
  "url": "https://…/invite/9f2c…e41a"
}
```

- `email` необязателен. Если он указан, принять приглашение можно только с
этим email.
- Приглашение действует 7 дней.
- Ссылку `url` передайте участнику: по ней он задаёт email и пароль в
кабинете и сразу получает свой API-ключ.


Принять приглашение можно и через API, без кабинета:

```bash
curl -X POST 'https://b2b.lumowallet.io/auth/invites/{token}/accept' \
  -H 'Content-Type: application/json' \
  -d '{"email": "accountant@acme.com", "password": "at-least-8-chars"}'
```

```json
{
  "company": {
    "id": "660e8400-e29b-41d4-a716-446655440001",
    "name": "Acme LLC",
    "email": "accountant@acme.com"
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "apiKey": "lb2b_7d0e9c8b6a5f4e3d2c1b0a9f8e7d6c5b"
}
```

Один email может состоять только в одной компании: если пользователь с таким
email уже есть, придёт `409 CONFLICT`.

`GET /auth/invites/{token}` без авторизации показывает, куда ведёт
приглашение: `companyId`, `companyName`, `email`, `role`, `permissions`,
`expiresAt` и `status` (`active` / `accepted` / `expired`).

Активные приглашения — `GET /members/invites`, отозвать —
`DELETE /members/invites/{id}`.

## Участники

```bash
curl 'https://b2b.lumowallet.io/members' -H 'X-API-Key: YOUR_API_KEY'
```

```json
[
  {
    "userId": "3c9e2f6a-1b7d-4c55-9a0e-7d2f4b8c1e90",
    "email": "owner@acme.com",
    "role": "owner",
    "permissions": ["*"],
    "joinedAt": "2026-03-01T10:00:00.000Z"
  },
  {
    "userId": "8a7b6c5d-4e3f-4a1b-9c8d-7e6f5a4b3c2d",
    "email": "accountant@acme.com",
    "role": "member",
    "permissions": ["orders.read", "balance.read"],
    "joinedAt": "2026-10-03T12:05:00.000Z"
  }
]
```

Изменить роль или права — `PATCH /members/{userId}` с полями `role`
(`admin` / `member`) и `permissions`, оба необязательные. Новый список прав
заменяет прежний целиком.

Удалить участника — `DELETE /members/{userId}`. Его API-ключ сразу
перестаёт работать.

Ограничения:

- свою роль менять нельзя (`403 CANNOT_CHANGE_OWN_ROLE`);
- менять или понижать владельца может только другой владелец
(`403 ROLE_CHANGE_NOT_ALLOWED`);
- нельзя удалить последнего владельца, и владелец не может удалить сам
себя (`403 ROLE_CHANGE_NOT_ALLOWED`).


Приглашать, менять и удалять участников можно только от имени участника —
с ключом компании старого образца эти запросы отвечают `400`.

## Ошибки

| HTTP | `errorCode` | Когда |
|  --- | --- | --- |
| 400 | `INVITE_USED` | Приглашение уже принято |
| 400 | `INVITE_EXPIRED` | Срок приглашения истёк или приглашение отозвано |
| 400 | `INVITE_INVALID` | Email не совпадает с указанным в приглашении |
| 403 | `CANNOT_CHANGE_OWN_ROLE` | Попытка изменить свою роль |
| 403 | `ROLE_CHANGE_NOT_ALLOWED` | Действие с владельцем, на которое нет полномочий |
| 404 | `INVITE_NOT_FOUND` | Приглашения с таким токеном или id нет |
| 404 | `MEMBER_NOT_FOUND` | Участника с таким `userId` в компании нет |
| 409 | `CONFLICT` | Пользователь с таким email уже существует |