# Action Endpoint (/operate/observability/api-reference/actions)



<Callout type="info" title="Use the SDKs for application code">
  This endpoint exists for **tooling and the built-in dashboard** — channel management and
  ad-hoc send/receive from an operator console. For application messaging — producing and
  consuming Events, Events Store, Queues, Commands, and Queries — use the
  [language SDKs](/deploy), which give you streaming, batching, acknowledgements,
  and reconnection that this single request endpoint does not.
</Callout>

All channel-management and message operations on the management API go through a single
endpoint, which dispatches on the `type` field. It lives on the management API port
(`:8080`), uses the [standard response envelope](/operate/observability/api-reference), and is
gated by the readiness check.

## POST /api/request [#post-apirequest]

The request body always has the same two top-level fields: an action `type` and an
action-specific `data` object.

```json
{
  "type": "<action_type>",
  "data": { }
}
```

| Field  | Type     | Required | Description                                   |
| ------ | -------- | -------- | --------------------------------------------- |
| `type` | `string` | Yes      | Action-type identifier (see the table below). |
| `data` | `object` | Yes      | Action-specific parameters.                   |

### Supported action types [#supported-action-types]

| Type                         | Description                                         | Returns data |
| ---------------------------- | --------------------------------------------------- | ------------ |
| `create_channel`             | Create a new channel.                               | No           |
| `delete_channel`             | Delete an existing channel.                         | No           |
| `send_queue_message`         | Send a message to a queue channel.                  | Yes          |
| `receive_queue_messages`     | Receive or peek messages from a queue.              | Yes          |
| `purge_queue_channel`        | Purge all messages from a queue.                    | Yes          |
| `send_pubsub_message`        | Publish an Events or Events Store message.          | No           |
| `send_cqrs_message_request`  | Send a command or query.                            | Yes          |
| `send_cqrs_message_response` | Respond to a received command or query.             | No           |
| `get_charts_array`           | Get time-series chart data for a channel or client. | Yes          |

## Channel management [#channel-management]

### create\_channel [#create_channel]

Creates a new channel of the given type. The channel is registered in metrics immediately.

```json
{
  "type": "create_channel",
  "data": {
    "type": "queues",
    "name": "orders.process"
  }
}
```

| Field  | Type     | Required | Description                                                                        |
| ------ | -------- | -------- | ---------------------------------------------------------------------------------- |
| `type` | `string` | Yes      | Channel type: `"queues"`, `"events"`, `"events_store"`, `"commands"`, `"queries"`. |
| `name` | `string` | Yes      | Channel name.                                                                      |

A success response carries `data: null`:

```json
{
  "error": false,
  "error_string": "",
  "data": null
}
```

### delete\_channel [#delete_channel]

Deletes an existing channel. This is a cluster-wide operation: the deletion is queued and
propagated across nodes, and the server waits up to 10 seconds for confirmation. Only
`queues` and `events_store` channels have durable storage to remove; `events`, `commands`,
and `queries` channels are cleared from metrics and topology only.

```json
{
  "type": "delete_channel",
  "data": {
    "type": "events_store",
    "channel": "notifications"
  }
}
```

| Field     | Type     | Required | Description                          |
| --------- | -------- | -------- | ------------------------------------ |
| `type`    | `string` | Yes      | Channel type. See the warning below. |
| `channel` | `string` | Yes      | Channel name to delete.              |

<Callout type="warn" title="Always include type when deleting">
  Server-side validation only checks that `channel` is non-empty — it does **not** validate
  `type`. But the downstream delete logic uses `type` to decide which durable storage is
  cleaned, which metric series are removed, and which topology entries are deleted. Omitting
  `type` returns a "successful" response with **incomplete cleanup** (storage and metrics are
  left behind). Always send `type` for a reliable delete.
</Callout>

A success response carries `data: null`. A delete can also fail with a cluster propagation
error or a `"timeout waiting for delete channel request"` if no confirmation arrives within
10 seconds — both returned in the envelope.

## Queue actions [#queue-actions]

### send\_queue\_message [#send_queue_message]

Sends a message to a queue channel.

```json
{
  "type": "send_queue_message",
  "data": {
    "messageId": "msg-001",
    "channel": "orders.process",
    "metadata": "order metadata",
    "body": "order payload data",
    "tags": "priority=high,region=us-east",
    "maxReceiveCount": 3,
    "maxReceiveQueue": "orders.dead-letter",
    "expirationAt": 60,
    "delayedTo": 10
  }
}
```

| Field             | Type     | Required | Description                                                     |
| ----------------- | -------- | -------- | --------------------------------------------------------------- |
| `messageId`       | `string` | No       | Custom message ID (auto-generated if empty).                    |
| `channel`         | `string` | Yes      | Target queue channel.                                           |
| `metadata`        | `string` | No       | Message metadata string.                                        |
| `body`            | `any`    | Yes      | Message body — a string, JSON object, or JSON array.            |
| `tags`            | `string` | No       | Comma-separated `key=value` pairs.                              |
| `maxReceiveCount` | `int`    | No       | Max delivery attempts before rerouting (`0` = unlimited).       |
| `maxReceiveQueue` | `string` | No       | Dead-letter queue for messages exceeding the max receive count. |
| `expirationAt`    | `int`    | No       | Message expiration in seconds.                                  |
| `delayedTo`       | `int`    | No       | Delay delivery by this many seconds.                            |

The response returns the assigned message ID and formatted timestamps:

```json
{
  "error": false,
  "error_string": "",
  "data": {
    "messageId": "msg-001",
    "sentAt": "2024-03-01T12:00:00Z",
    "expiresAt": "2024-03-01 12:01:00",
    "delayedTo": "2024-03-01 12:00:10"
  }
}
```

### receive\_queue\_messages [#receive_queue_messages]

Receives — or peeks at — messages from a queue channel.

```json
{
  "type": "receive_queue_messages",
  "data": {
    "channel": "orders.process",
    "isPeek": false,
    "count": 10
  }
}
```

| Field     | Type      | Required | Description                                                                      |
| --------- | --------- | -------- | -------------------------------------------------------------------------------- |
| `channel` | `string`  | Yes      | Queue channel to receive from.                                                   |
| `isPeek`  | `boolean` | No       | If `true`, peek without consuming — messages stay in the queue. Default `false`. |
| `count`   | `int`     | No       | Maximum messages to receive (must be `> 0` if set).                              |

The response `data` is an array of messages:

```json
{
  "error": false,
  "error_string": "",
  "data": [
    {
      "messageId": "msg-001",
      "clientId": "order-service",
      "metadata": "order metadata",
      "body": "order payload data",
      "timestamp": 1709312400000,
      "sequence": 42,
      "tags": "{\"priority\":\"high\"}",
      "receivedCount": 1,
      "reRoutedFrom": "",
      "expirationAt": 1709312460000,
      "delayedTo": 0
    }
  ]
}
```

The `body` field is auto-detected: a valid JSON object or array is returned as such,
otherwise as a string.

### purge\_queue\_channel [#purge_queue_channel]

Acknowledges and removes all waiting messages from a queue channel.

```json
{
  "type": "purge_queue_channel",
  "data": {
    "channel": "orders.process"
  }
}
```

| Field     | Type     | Required | Description             |
| --------- | -------- | -------- | ----------------------- |
| `channel` | `string` | Yes      | Queue channel to purge. |

The response returns the number of messages purged:

```json
{
  "error": false,
  "error_string": "",
  "data": {
    "count": 150
  }
}
```

## Pub/Sub action [#pubsub-action]

### send\_pubsub\_message [#send_pubsub_message]

Publishes a message to an Events or Events Store channel. The `isEvents` flag selects which.

```json
{
  "type": "send_pubsub_message",
  "data": {
    "messageId": "evt-001",
    "channel": "notifications",
    "metadata": "event metadata",
    "body": { "event": "user.created", "userId": 123 },
    "tags": "source=api,priority=normal",
    "isEvents": true
  }
}
```

| Field       | Type      | Required | Description                                                                        |
| ----------- | --------- | -------- | ---------------------------------------------------------------------------------- |
| `messageId` | `string`  | No       | Custom event ID.                                                                   |
| `channel`   | `string`  | Yes      | Target channel name.                                                               |
| `metadata`  | `string`  | No       | Event metadata.                                                                    |
| `body`      | `any`     | Yes      | Event body — a string, JSON object, or JSON array.                                 |
| `tags`      | `string`  | No       | Comma-separated `key=value` pairs.                                                 |
| `isEvents`  | `boolean` | No       | `true` for transient Events, `false` for persistent Events Store. Default `false`. |

The `isEvents` flag controls the pattern:

* `true` — the message goes to the **Events** channel (fire-and-forget, no persistence). Only online subscribers receive it.
* `false` — the message goes to the **Events Store** channel (persistent). Subscribers can replay from any point.

A success response carries `data: null`.

## CQRS actions [#cqrs-actions]

### send\_cqrs\_message\_request [#send_cqrs_message_request]

Sends a command or query to a channel and waits for a response. The `isCommands` flag
selects the pattern; `timeout` is required.

```json
{
  "type": "send_cqrs_message_request",
  "data": {
    "requestId": "req-001",
    "channel": "user-service",
    "metadata": "get-user",
    "body": { "userId": 123 },
    "tags": "version=2",
    "isCommands": false,
    "timeout": 30
  }
}
```

| Field        | Type      | Required | Description                                                                                       |
| ------------ | --------- | -------- | ------------------------------------------------------------------------------------------------- |
| `requestId`  | `string`  | No       | Custom request ID.                                                                                |
| `channel`    | `string`  | Yes      | Target channel.                                                                                   |
| `metadata`   | `string`  | No       | Request metadata.                                                                                 |
| `body`       | `any`     | Yes      | Request body.                                                                                     |
| `tags`       | `string`  | No       | Comma-separated `key=value` pairs.                                                                |
| `isCommands` | `boolean` | No       | `true` for a command (fire-and-confirm), `false` for a query (request-response). Default `false`. |
| `timeout`    | `int`     | Yes      | Response timeout in **seconds** (must be `> 0`).                                                  |

A **query** response includes the responder's body and metadata:

```json
{
  "error": false,
  "error_string": "",
  "data": {
    "metadata": "user-data",
    "body": { "name": "John", "email": "john@example.com" },
    "tags": "{\"version\":\"2\"}",
    "timestamp": 1709312400000,
    "executed": true,
    "error": ""
  }
}
```

A **command** response carries only the execution status — no body:

```json
{
  "error": false,
  "error_string": "",
  "data": {
    "tags": "",
    "timestamp": 1709312400000,
    "executed": true,
    "error": ""
  }
}
```

### send\_cqrs\_message\_response [#send_cqrs_message_response]

Sends a response to a previously received command or query — used by a subscriber that is
processing requests over a [WebSocket](/operate/observability/api-reference/websockets).

```json
{
  "type": "send_cqrs_message_response",
  "data": {
    "requestId": "req-001",
    "replyChannel": "reply-abc123",
    "metadata": "response metadata",
    "body": { "result": "success" },
    "tags": "processed=true",
    "executed": true,
    "error": ""
  }
}
```

| Field          | Type      | Required | Description                                                                                                                                                                          |
| -------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `requestId`    | `string`  | No       | The original request ID being responded to.                                                                                                                                          |
| `replyChannel` | `string`  | Yes      | The reply channel supplied on the received request (the subscriber receives it on the incoming request message). Echo it back unchanged so the response reaches the original sender. |
| `metadata`     | `string`  | No       | Response metadata.                                                                                                                                                                   |
| `body`         | `any`     | Yes      | Response body.                                                                                                                                                                       |
| `tags`         | `string`  | No       | Comma-separated `key=value` pairs.                                                                                                                                                   |
| `executed`     | `boolean` | No       | Whether execution succeeded.                                                                                                                                                         |
| `error`        | `string`  | No       | Error message if execution failed.                                                                                                                                                   |

A success response carries `data: null`.

## Chart data [#chart-data]

### get\_charts\_array [#get_charts_array]

Retrieves time-series chart data for a channel or client. The response is four aligned
series — incoming/outgoing message counts and volumes — ready to plot.

```json
{
  "type": "get_charts_array",
  "data": {
    "channel": "queues/orders.process",
    "time_range": "1h",
    "time_zone": 300
  }
}
```

| Field        | Type     | Required                    | Description                                                                                                                                                            |
| ------------ | -------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channel`    | `string` | One of `channel` / `client` | Channel identifier, format `{type}/{name}`.                                                                                                                            |
| `client`     | `string` | One of `channel` / `client` | Client identifier, format `{type}/{channel}/{clientId}`.                                                                                                               |
| `time_range` | `string` | Yes                         | One of `"1h"`, `"2h"`, `"6h"`, `"12h"`, `"1d"`, `"7d"`, `"14d"`, `"30d"`.                                                                                              |
| `time_zone`  | `int`    | No                          | Timezone offset in **minutes** — pass the browser's `getTimezoneOffset()` value (e.g. `300` for UTC-5, `0` for UTC, `-330` for UTC+5:30). Applied to the chart labels. |

The response carries four `ChartDataDTO` series that share one label set for alignment:

```json
{
  "error": false,
  "error_string": "",
  "data": {
    "incomingMessages": {
      "labels": ["12:00", "12:05", "12:10", "12:15"],
      "data": ["150", "200", "175", "180"]
    },
    "incomingVolume": {
      "labels": ["12:00", "12:05", "12:10", "12:15"],
      "data": ["30720", "40960", "35840", "36864"]
    },
    "outgoingMessages": {
      "labels": ["12:00", "12:05", "12:10", "12:15"],
      "data": ["145", "198", "170", "178"]
    },
    "outgoingVolume": {
      "labels": ["12:00", "12:05", "12:10", "12:15"],
      "data": ["29696", "40550", "34816", "36454"]
    }
  }
}
```

Feed `labels` as the x-axis and `data` as the y-axis to any charting library. See
[Data Models](/operate/observability/api-reference/data-models) for the `ChartsArrayDTO` and
`ChartDataDTO` schemas.

## Errors [#errors]

Every action returns errors in the standard envelope with `error: true` — like all
business-logic errors, these come back with HTTP `200` (see
[status codes](/operate/observability/api-reference#http-status-codes)).

```json
{
  "error": true,
  "error_string": "descriptive error message",
  "data": null
}
```

Common cross-action errors:

| `error_string`                                | Cause                                           |
| --------------------------------------------- | ----------------------------------------------- |
| `"unknown action type: <type>"`               | Unrecognized `type` field.                      |
| `"api service not ready"`                     | The management API is still initializing.       |
| `"the broker is not ready to accept traffic"` | The broker is not yet ready to send or receive. |

Each action also has its own validation errors — for example a missing `channel`, a missing
`body`, a malformed tag, or a non-positive `count` or `timeout`. These too are returned with
HTTP `200` and `error: true`.

## Related [#related]

<Cards>
  <Card title="Management API" href="/operate/observability/api-reference" description="The response envelope, status-code behavior, and access control shared by every endpoint." />

  <Card title="WebSocket Protocols" href="/operate/observability/api-reference/websockets" description="Subscribe to commands and queries, then respond with send_cqrs_message_response." />

  <Card title="Data Models" href="/operate/observability/api-reference/data-models" description="Schemas for the chart and message DTOs returned by these actions." />
</Cards>
