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

# Custom client

> Call MastermindCMS APIs directly over WebSocket/STOMP and REST

Use this approach when you want full control over networking (your own STOMP client, your own REST layer, your own auth/token storage).

## WebSocket API (STOMP)

MastermindCMS exposes a STOMP broker over WebSocket:

* Native WebSocket endpoint: `/ws`
* SockJS fallback endpoint: `/sock`
* Application destination prefix: `/request`
* Broker destinations: `/topic/**`, `/user/topic/**`, and `/queue/**`

### Subscribe

Default JSON responses:

* `/topic/msm/json`
* `/user/topic/msm/json`

Default HTML (SSR) render responses:

* `/topic/msm/render`
* `/user/topic/msm/render`

The backend can also publish domain events to custom destinations under `/topic/**` (for example `/topic/jobs`, `/topic/job/{id}`, `/topic/order/{id}`, `/topic/customer/{id}`).

### Publish destinations

Render (SSR):

| Destination                | Purpose                                      |
| -------------------------- | -------------------------------------------- |
| `/request/msm/render`      | Render the current page or specific elements |
| `/request/beans/invoke`    | Call a service method and then render        |
| `/request/beans/update`    | Update a bean and then render                |
| `/request/repository/call` | Repository calls and then render             |
| `/request/documents`       | Database document operations and then render |

JSON:

| Destination                       | Purpose                                                |
| --------------------------------- | ------------------------------------------------------ |
| `/request/json/bean/INVOKE`       | Call a backend service method (reflection-based)       |
| `/request/json/bean/UPDATE`       | Update a backend bean (payload-based)                  |
| `/request/json/repository/INVOKE` | Read from a repository                                 |
| `/request/json/repository/UPDATE` | Write/update via a repository                          |
| `/request/json/database`          | Database document operations (`READ`, `ADD`, `REMOVE`) |
| `/request/notifier`               | Publish a notification payload to a topic              |

### Request and response shape

All WebSocket requests are based on `BasicRequestMessage`:

* `path`: current page path (request context)
* `payload`: optional data (depends on the destination)
* `elements`: optional list of element payloads for partial updates
* `actionId`: optional correlation id (recommended)
* `eventType`: `USER`, `SHARED`, or `GLOBAL`
* `sharedEndpoint`: optional logical endpoint name (the backend responds to `/topic/<sharedEndpoint>`)

Specialized request messages add fields:

* `BeanRequestMessage`: `scope`, `beanId`, `functionName`, `args`
* `RepositoryRequestMessage`: `repositoryId`, `requestType`
* `DocumentRequestMessage`: `databaseName`, `collectionName`, `requestType`

JSON responses use:

```json theme={null}
{
  "result": {},
  "actionId": "a1b2c3d4e5f6"
}
```

### Example (browser)

```js theme={null}
import { Client } from "@stomp/stompjs";
import SockJS from "sockjs-client";

const client = new Client({
  // brokerURL: "wss://example.com/ws",
  webSocketFactory: () => new SockJS("/sock"),
  reconnectDelay: 2000,
});

client.onConnect = () => {
  client.subscribe("/user/topic/msm/json", (msg) => {
    console.log("JSON response", JSON.parse(msg.body));
  });

  const actionId = Math.random().toString(16).slice(2, 14);

  client.publish({
    destination: "/request/json/bean/INVOKE",
    headers: {
      // Optional: JWT as a native STOMP header
      // Authorization: "Bearer <token>",
    },
    body: JSON.stringify({
      path: "/current/page",
      scope: "PROTOTYPE",
      beanId: "someServiceImpl",
      functionName: "someMethod",
      args: [{ 0: { query: {}, language: "en" } }],
      actionId,
      eventType: "USER",
    }),
  });
};

client.activate();
```

### Authorization notes

* JSON handlers can accept a JWT via the native header `Authorization: Bearer <token>`.
* Some operations require elevated permissions (for example repository updates and database writes). If access is denied, the backend responds with `{ "error": "Access denied", ... }`.

## REST API

REST is used for:

* authentication (`/api/v1/authenticate`, `/api/v1/logout`, token operations)
* file and data operations (upload/remove/download)
* report export and asset listing
* integrations (for example, delivery/payment providers)
* HTTP fallback for service calls (`/api/v1/bean/request`)

### Common headers

Some endpoints (notably `/api/v1/bean/request`) require request context headers:

| Header          | Example          | Purpose               |
| --------------- | ---------------- | --------------------- |
| `Site-Context`  | `my-site`        | Site context          |
| `Lang-Context`  | `en`             | Language context      |
| `Authorization` | `Bearer <token>` | Optional bearer token |

### Authentication

`POST /api/v1/authenticate` authenticates a user (typically via session/remember-me cookies).

To obtain a JWT token for API calls, use `POST /api/v1/auth/token` (returns `{ token, expires }`). You can validate a token with `POST /api/v1/auth/validate-token`.

`POST /api/v1/logout` logs out the current session and expects a JSON body that includes `role` (for example `"user"` or `"admin"`).

### Unified service call over HTTP

`POST /api/v1/bean/request` calls a backend bean method using the same reflection-based mechanism as the WebSocket API.

Required headers:

* `Site-Context`
* `Lang-Context`

Request example:

```json theme={null}
{
  "beanId": "someServiceImpl",
  "scope": "PROTOTYPE",
  "functionName": "someMethod",
  "args": [
    {
      "0": {
        "query": {},
        "language": "en"
      }
    }
  ]
}
```

Response example:

```json theme={null}
{
  "result": {}
}
```

### Uploads, downloads, assets, reports

| Endpoint                      | Method | Purpose                                                                                    |
| ----------------------------- | ------ | ------------------------------------------------------------------------------------------ |
| `/api/v1/uploadImage`         | `POST` | Upload an image (multipart: `file`, `payload`, optional `command`)                         |
| `/api/v1/removeImage`         | `POST` | Remove an uploaded image (multipart: `payload`, optional `command`)                        |
| `/api/v1/uploadData`          | `POST` | Upload a file/document (multipart: `file`, `payload`, optional `id`, optional `command`)   |
| `/api/v1/removeDocument`      | `POST` | Remove an uploaded file/document (multipart: `payload`, optional `id`, optional `command`) |
| `/api/v1/downloadData`        | `GET`  | Download a file/document (query: `pathName`, optional `fileName`)                          |
| `/api/v1/images`              | `POST` | List images for an asset manager (multipart: `path`, `urlPrefix`)                          |
| `/api/v1/downloadReportsData` | `POST` | Download a generated report (multipart: `payload`, `command`)                              |

Example: upload an image

```js theme={null}
const formData = new FormData();
formData.append("file", file);
formData.append("payload", JSON.stringify({ destination: "/local/images" }));
formData.append("command", "UPLOAD_IMAGE");

const res = await fetch("/api/v1/uploadImage", { method: "POST", body: formData });
const json = await res.json(); // e.g. { url: "...", name: "..." }
```

Example: export a report

```js theme={null}
const formData = new FormData();
formData.append("payload", JSON.stringify({ fileName: "report.csv", searchRequest: {/* ... */} }));
formData.append("command", "EXPORT_SEARCH_DATA_DUMP");

const res = await fetch("/api/v1/downloadReportsData", { method: "POST", body: formData });
const blob = await res.blob();
```

### Delivery and payment integrations

Some integrations are exposed as REST endpoints and used by corresponding UI components. Examples include:

* `/api/v1/cdek` (delivery widget service/proxy)
* `/api/v1/stripe` (Stripe payment webhook)
* `/api/v1/yookassa` (payment integration)

### Password reset and verification

If email/password flows are enabled, MastermindCMS can expose endpoints such as:

* `/api/v1/auth/verify`
* `/api/v1/auth/reset-password`
* `/api/v1/auth/change-password`
* `/api/v1/auth/save-password`
