> 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 or a count. Can be passed without <code>unit</code></td></tr><tr><td><code>unit</code></td><td>string</td><td>no (rejected if passed without <code>value_num</code>)</td><td>Unit of measurement for the value. Up to 3 characters - for example <code>RUB</code>, <code>USD</code>, <code>kg</code>. Requires <code>value_num</code> to also be set</td></tr><tr><td><code>value_str</code></td><td>string</td><td>no</td><td>String value of the event - for example, a promo code or plan name. Up to 250 characters. Stored separately from <code>value_num</code>/<code>unit</code>, can be passed together with them</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>

> `value_num` can be passed without `unit` (e.g. a plain count or score with no unit). `unit` without `value_num`, however, is rejected - a unit of measurement with no value to attach to doesn't make sense, so the request will fail with a `400` error.

> `value_num` and `value_str` are independent of each other and are stored in separate fields - you can pass both at once, e.g. a numeric order amount and a text plan code on the same event.

#### 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" },
  { "event": "items_viewed", "user_id": 123456789, "value_num": 5 }
]
```

{% endcode %}

#### Example call - string value

{% code overflow="wrap" %}

```json
{
  "event": "promo_code_applied",
  "user_id": 123456789,
  "value_str": "SUMMER20"
}
```

{% endcode %}

#### Example call - numeric and string value together

{% code overflow="wrap" %}

```json
{
  "event": "order_paid",
  "user_id": 123456789,
  "value_num": 1490.50,
  "unit": "RUB",
  "value_str": "premium_plan"
}
```

{% 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>unit</code> is specified, but <code>value_num</code> is not</td><td>Add the <code>value_num</code> the unit applies to, or remove <code>unit</code></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>value_str</code> is not a string or is longer than 250 characters</td><td>Shorten the value or pass it as a string</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": "value_num is missing"
}
```

{% endcode %}
