# Managers - X-Radius API

> Base URL: https://x-radius.com/api/v1
> Auth: Authorization: Bearer xrt_...  (a manager API token)
> Envelope: {"data": ...}; lists add {"meta":{page,page_size,total,has_next}}
> Errors: {"error":{"code","message","request_id"}} - branch on code, never on message
> Timestamps: yyyy-MM-dd HH:mm:ss, UTC
> Money: a bare JSON number in major units, with an ISO-4217 currency code beside it
> Idempotency: redeem and activate endpoints take a client-supplied request_id (UUID)
>
> This page: https://x-radius.com/docs/api/managers
> Every group: https://x-radius.com/llms.txt

32 endpoints in 1 resource groups. 14 carry a hand-written reference entry with examples; the remaining 18 are generated from the running router and carry method, path, authentication, permission and rate-limit bucket, but no request or response example.

### Assign groups

`POST /api/v1/admin/managers/bulk-group`

- Authentication: manager session (JWT) or API token
- Permission: `prm_groups_assign` (Assign groups)
- Risk: write
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Assign groups

`POST /api/v1/admin/managers/{id}/group`

- Authentication: manager session (JWT) or API token
- Permission: `prm_groups_assign` (Assign groups)
- Risk: write
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### List managers

`GET /api/v1/managers`

One page of managers. A caller without tenant-wide visibility sees their own descendants only — never their parent, never a sibling branch.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_index` (View managers)
- Risk: read

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `filter[parent_id]` | query | integer | no | Direct children of one manager. Also accepted as a bare parent_id query parameter. Narrows within the caller's subtree, never past it. |
| `q` | query | string | no | Searches name, username and email. |

#### Response — 200 OK

```json
{
  "data": [
    {
      "id": 57,
      "tenant_id": 12,
      "email": "giza@acme.example",
      "username": "giza-reseller",
      "firstname": "Mona",
      "status": "active",
      "parent_manager_id": 41,
      "parent_username": "cairo-reseller",
      "balance": 1840.00,
      "debt_limit": 5000.00,
      "debt": 0.00,
      "discount_rate": 10.00,
      "points_balance": 240,
      "users_count": 312,
      "created_at": "2026-05-02 09:00:00"
    }
  ],
  "meta": { "page": 1, "page_size": 50, "total": 9, "has_next": false }
}
```

#### Note

debt, parent_username, users_count and the other joined fields are computed by the list projection only. Fetching one manager by id returns the same record without them, so a client that reads users_count off a detail response will always see zero.


### Create a manager

`POST /api/v1/managers`

Creates a staff or reseller account. A manager needs at least one login identity, so email or username must be present.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_create` (Create managers)
- Risk: write
- Rate limit bucket: `t_mutate`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `email` | body | string | no | Login email. Unique within the tenant. Required unless username is given. |
| `username` | body | string | no | Login username. Required unless email is given. |
| `password` | body | string | yes | Hashed with Argon2id before storage, unlike a subscriber's. |
| `parent_manager_id` | body | integer | no | Defaults to the caller. Never null — a tenant keeps exactly one root manager, its owner. A named parent must be one the caller may act on. |

#### Request

```json
{
  "email": "giza@acme.example",
  "username": "giza-reseller",
  "password": "a-strong-password",
  "firstname": "Mona",
  "parent_manager_id": 41
}
```

#### Errors

| Code | Status | When |
| --- | --- | --- |
| `ERR_CONFLICT` | 409 | the email is already registered in this tenant |
| `ERR_CONFLICT` | 409 | the licensed manager ceiling is reached |


### List managers for a picker

`GET /api/v1/managers/all`

Id and name only, un-paginated, for populating a select control. Same subtree scope as the full list.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_index` (View managers)
- Risk: read


### Edit managers

`POST /api/v1/managers/bulk-activate`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_update` (Edit managers)
- Risk: write
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Delete managers

`POST /api/v1/managers/bulk-delete`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_delete` (Delete managers)
- Risk: danger
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Edit managers

`POST /api/v1/managers/bulk-suspend`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_update` (Edit managers)
- Risk: write
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Delete a manager

`DELETE /api/v1/managers/{id}`

Hard-deletes the manager. Refused while they are still referenced by subscribers, card stock or ledger records — move those first.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_delete` (Delete managers)
- Risk: danger
- Rate limit bucket: `t_mutate`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. |

#### Errors

| Code | Status | When |
| --- | --- | --- |
| `ERR_VALIDATION` | 422 | the manager is still in use, or you are trying to delete yourself |
| `ERR_NOT_FOUND` | 404 | no such manager, or one outside the caller's subtree |


### Fetch one manager

`GET /api/v1/managers/{id}`

The manager record, including their custom form fields.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_index` (View managers)
- Risk: read

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. |

#### Errors

| Code | Status | When |
| --- | --- | --- |
| `ERR_NOT_FOUND` | 404 | no such manager, or one outside the caller's subtree |


### Update a manager

`PATCH /api/v1/managers/{id}`

Partial update. An omitted field is left alone.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_update` (Edit managers)
- Risk: write
- Rate limit bucket: `t_mutate`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. |

#### Errors

| Code | Status | When |
| --- | --- | --- |
| `ERR_NOT_FOUND` | 404 | no such manager, or one outside the caller's subtree |


### Edit managers

`POST /api/v1/managers/{id}/2fa/reset`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_update` (Edit managers)
- Risk: write
- Rate limit bucket: `t_mutate`
- Not available to API tokens: this route requires an interactive manager session

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Edit managers

`POST /api/v1/managers/{id}/activate`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_update` (Edit managers)
- Risk: write
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Read a manager's debt

`GET /api/v1/managers/{id}/debt`

Outstanding debt for this manager, read off the loan ledger.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_journal` (View manager journal)
- Risk: read

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. |


### View manager invoices

`GET /api/v1/managers/{id}/invoices`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_invoices` (View manager invoices)
- Risk: read

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Read a manager's ledger

`GET /api/v1/managers/{id}/journal`

Every money movement on this manager's wallet, newest first, with a running balance. This is the authoritative record — the wallet balance is its consequence, not a parallel truth.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_journal` (View manager journal)
- Risk: read

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. |


### View manager journal

`GET /api/v1/managers/{id}/loans`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_journal` (View manager journal)
- Risk: read

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Log in as manager

`POST /api/v1/managers/{id}/login-as`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_login_as` (Log in as manager)
- Risk: danger
- Rate limit bucket: `t_mutate`
- Not available to API tokens: this route requires an interactive manager session

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### View managers

`GET /api/v1/managers/{id}/overview`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_index` (View managers)
- Risk: read

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### View manager receipts

`GET /api/v1/managers/{id}/payments`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_receipts` (View manager receipts)
- Risk: read

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Add / deduct manager reward points

`POST /api/v1/managers/{id}/points/add`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_reward_points` (Add / deduct manager reward points)
- Risk: write
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Add / deduct manager reward points

`POST /api/v1/managers/{id}/points/deduct`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_reward_points` (Add / deduct manager reward points)
- Risk: write
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### View managers

`GET /api/v1/managers/{id}/profile`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_index` (View managers)
- Risk: read

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### View manager receipts

`GET /api/v1/managers/{id}/receipts`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_receipts` (View manager receipts)
- Risk: read

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Edit managers

`POST /api/v1/managers/{id}/reset-password`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_update` (Edit managers)
- Risk: write
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Read a manager's roles

`GET /api/v1/managers/{id}/roles`

The roles assigned to this manager, which is what their permissions resolve from.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_index` (View managers)
- Risk: read

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. |


### Replace a manager's roles

`PUT /api/v1/managers/{id}/roles`

Replaces the whole set. A caller may only grant roles whose permissions are a subset of their own, so this cannot be used to escalate.

- Authentication: manager session (JWT) or API token
- Permission: `prm_roles_manage` (Manage roles & permissions)
- Risk: danger
- Rate limit bucket: `t_mutate`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. |
| `role_ids` | body | array | yes | The complete new set of role ids. |

#### Errors

| Code | Status | When |
| --- | --- | --- |
| `ERR_FORBIDDEN` | 403 | the caller holds an API token — this route refuses machine credentials |

#### Note

This takes effect on every API token that manager holds, immediately and silently. A token's authority is its owner's live permissions intersected with its own scope, so removing a role here shrinks every integration running as that person with no notification and no change to the token record.


### Suspend a manager

`POST /api/v1/managers/{id}/suspend`

Sets the account to suspended. They can no longer sign in, and every API token they own stops authenticating with reason api_token_owner_inactive.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_update` (Edit managers)
- Risk: write
- Rate limit bucket: `t_mutate`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. |

#### Note

Suspending a reseller does not touch the subscribers below them. Those keep connecting; only the person's own access ends.


### Credit a manager wallet

`POST /api/v1/managers/{id}/wallet/deposit`

Moves money into a reseller's wallet and posts the matching ledger entry. Idempotent on request_id through the ledger's own unique constraint.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_deposit` (Deposit to manager wallet)
- Risk: danger
- Rate limit bucket: `t_mutate`
- Idempotent on `request_id`: retrying with the same id returns the original result

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. May not be the caller themselves. |
| `amount` | body | number | yes | Major units, a bare JSON number. |
| `request_id` | body | string | yes | Idempotency key. |
| `reason` | body | string | no | Free-text note recorded on the ledger row. |
| `issue_invoice` | body | boolean | no | Also record the human-facing accounting document. Defaults to TRUE when omitted on the manager side, unlike the subscriber side. |

#### Request

```json
{
  "amount": 5000.00,
  "reason": "bank transfer 2026-09-20",
  "request_id": "e1a7b204-1c4f-4a2e-8d66-90b3f7c1d452"
}
```

#### Errors

| Code | Status | When |
| --- | --- | --- |
| `ERR_CONFLICT` | 409 | the request_id is already held by a different ledger line |

#### Note

issue_invoice defaults differently here than on the subscriber wallet. Omitting it on a manager deposit issues the document; omitting it on a subscriber deposit does not. Send it explicitly if you care either way.


### Settle a manager's debt

`POST /api/v1/managers/{id}/wallet/pay-debt`

Records a repayment against a creditor. The creditor defaults to the debtor's parent manager.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_pay_debt` (Settle manager debt)
- Risk: danger
- Rate limit bucket: `t_mutate`
- Idempotent on `request_id`: retrying with the same id returns the original result

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | The debtor. May not be the caller themselves. |
| `amount` | body | number | yes | Major units. |
| `creditor_manager_id` | body | integer | no | Which creditor is being repaid. Absent means the debtor's parent. |
| `request_id` | body | string | yes | Idempotency key. |


### Deposit to manager wallet

`POST /api/v1/managers/{id}/wallet/topup`

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_deposit` (Deposit to manager wallet)
- Risk: danger
- Rate limit bucket: `t_mutate`

_This endpoint has no hand-written reference entry yet. The method, path, authentication, permission and rate limit above are generated from the running router and are accurate; there is no request or response example._


### Debit a manager wallet

`POST /api/v1/managers/{id}/wallet/withdraw`

The inverse of deposit, posting the matching ledger entry.

- Authentication: manager session (JWT) or API token
- Permission: `prm_managers_withdrawal` (Withdraw from manager wallet)
- Risk: danger
- Rate limit bucket: `t_mutate`
- Idempotent on `request_id`: retrying with the same id returns the original result

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | integer | yes | Manager id. |
| `amount` | body | number | yes | Major units. |
| `request_id` | body | string | yes | Idempotency key. |
| `allow_negative` | body | boolean | no | Permit the withdrawal to drive the balance below zero. |


