> For the complete documentation index, see [llms.txt](https://docs.graspil.com/en/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.graspil.com/en/api/send-events.md).

# Sending custom events

Sends an arbitrary custom event to the system - for example, "paid for an order," "completed onboarding," "opened the catalog section."

This is the most flexible way to send an event: it has no strict binding to a chat/resource (unlike `send-target`) - just the event name and, optionally, its "value" and category.

### Request

{% code overflow="wrap" lineNumbers="true" expandable="true" %}

```html
POST /v1/send-event
```

{% endcode %}

Method - `POST`, body - JSON. You can pass:

* **a single** event object, or
* **an array of objects**, if you need to send several events in one request.

#### Event parameters

<table><thead><tr><th width="151.46484375">Parameter</th><th width="111.49609375">Type</th><th width="211.9375">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>event</code></td><td>string</td><td>yes</td><td>Event name. Up to 250 characters. You choose it yourself - for example <code>order_paid</code>, <code>onboarding_completed</code></td></tr><tr><td><code>user_id</code></td><td>int</td><td>yes</td><td>Telegram ID of the user who performed the event</td></tr><tr><td><code>value_num</code></td><td>number</td><td>no</td><td>Numeric value of the event - for example, the order amount. If specified, <code>unit</code> becomes required</td></tr><tr><td><code>unit</code></td><td>string</td><td>no (required if <code>value_num</code> is specified)</td><td>Unit of measurement for the value. Up to 3 characters - for example <code>RUB</code>, <code>USD</code>, <code>kg</code></td></tr><tr><td><code>category</code></td><td>string</td><td>no</td><td>An arbitrary event category - for grouping in reports. Up to 250 characters</td></tr><tr><td><code>webapp_name</code></td><td>string</td><td>no</td><td>The name of the Mini App in which the event occurred (if applicable). Up to 250 characters</td></tr><tr><td><code>date</code></td><td>string</td><td>no</td><td>Date and time of the event in ISO 8601 format, for example <code>2026-06-18T10:00:00.000+03:00</code>. If not passed, the current time at the moment the request is received will be used</td></tr></tbody></table>

> If `value_num` is specified but `unit` is not (or, conversely, `value_num` is not specified but a long `unit` is passed), the request will be rejected with a `400` error.

#### Example call - a single event

{% code overflow="wrap" %}

```json
{
  "event": "order_paid",
  "user_id": 123456789,
  "value_num": 1490.50,
  "unit": "RUB",
  "category": "payments"
}
```

{% endcode %}

#### Example call - several events at once

{% code overflow="wrap" %}

```json
[
  { "event": "onboarding_completed", "user_id": 123456789 },
  { "event": "order_paid", "user_id": 123456789, "value_num": 1490.5, "unit": "RUB" }
]
```

{% endcode %}

***

### Response

{% code overflow="wrap" %}

```json
{
  "ok": true
}
```

{% endcode %}

This method does not return an event ID or any other data - a successful `"ok": true` response means that all submitted events were accepted for processing. The actual processing (writing to analytics) happens asynchronously, a bit later.

> If the request contains an array of several events and one of them is invalid, the entire request will be rejected with an error, and none of the events in the array will be saved.

***

### Possible errors

<table><thead><tr><th width="109.9765625">Code</th><th>When it occurs</th><th>How to fix it</th></tr></thead><tbody><tr><td><code>400</code></td><td>Request body is not valid JSON</td><td>Make sure you're sending valid JSON and the <code>Content-Type: application/json</code> header</td></tr><tr><td><code>400</code></td><td><code>event</code> is missing</td><td>Add the <code>event</code> field</td></tr><tr><td><code>400</code></td><td><code>event</code> is not a string or is longer than 250 characters</td><td>Shorten the event name or pass it as a string</td></tr><tr><td><code>400</code></td><td><code>user_id</code> is missing</td><td>Add the <code>user_id</code> field</td></tr><tr><td><code>400</code></td><td><code>user_id</code> is not a number</td><td>Pass the Telegram ID as an integer, without quotes</td></tr><tr><td><code>400</code></td><td><code>value_num</code> is specified, but <code>unit</code> is not</td><td>Add <code>unit</code> if you're passing a numeric event value</td></tr><tr><td><code>400</code></td><td><code>value_num</code> is not a number</td><td>Pass the number without quotes</td></tr><tr><td><code>400</code></td><td><code>unit</code> is longer than 3 characters</td><td>Use a short unit notation (for example <code>RUB</code> instead of "rubles")</td></tr><tr><td><code>400</code></td><td><code>category</code> is not a string or is longer than 250 characters</td><td>Shorten the category or pass it as a string</td></tr><tr><td><code>400</code></td><td><code>webapp_name</code> is longer than 250 characters</td><td>Shorten the Mini App name</td></tr><tr><td><code>400</code></td><td><code>date</code> is specified but not recognized as a date</td><td>Pass the date in ISO 8601 format, for example <code>2026-06-18T10:00:00.000+03:00</code>, or omit the field entirely</td></tr><tr><td><code>401</code></td><td>API key is missing or invalid</td><td>Check the <code>Api-Key</code> header / <code>api-key</code> parameter</td></tr><tr><td><code>500</code></td><td>Internal server error</td><td>Retry the request later; if it keeps happening, contact support</td></tr></tbody></table>

**Error example:**

{% code overflow="wrap" %}

```json
{
  "ok": false,
  "error_code": 400,
  "error": "unit is missing"
}
```

{% endcode %}
