# Destinations (/docs/destinations)

A destination forwards matching events the moment they arrive. Your game sends events to RoChart, and other tools get the ones they need. **Destinations** in the sidebar lists them with how many events each one has delivered.

## Discord [#discord]

Post the events you care about to a Discord channel, such as every purchase over 1,000 Robux or every script error.

1. In Discord, open the channel's **Settings → Integrations → Webhooks → New Webhook**, and **Copy Webhook URL**. It looks like `https://discord.com/api/webhooks/123…/abc…` (`discordapp.com` works too).
2. **New destination → Discord**, then paste the URL.
3. Write the message, then **Send test**.

Each event arrives as an embed. The event name is the title and your message is the text (Discord markdown works). The player and the event's properties become fields, such as **Robux 199** and **Product 500 Gems**. The SDK's context properties (`$session_id`, `$server_id` …) are left out, and the footer reads *RoChart · your game*. Mentions are switched off, so event data can never ping `@everyone`.

```
**{{person.properties.name}}** bought {{properties.$product_name}} for {{properties.$robux}} Robux
```

| Variable | Is |
| --- | --- |
| `{{event}}` | The event name |
| `{{distinct_id}}` | The id the event was sent with: the player's UserId |
| `{{properties.key}}` | An event property, such as `$robux` |
| `{{person.properties.key}}` | A property of the player, such as their `name` |
| `{{timestamp}}` | When it happened |

A variable with no value shows as `—`. Discord takes about 30 messages a minute per channel. RoChart sends one at a time, and when Discord answers `429` it waits the `retry_after` Discord asks for before trying again. So pick the events that matter rather than every event.

<Callout type="info" title="Discord via Slack">
  Older setups used a Slack destination with the Discord URL plus `/slack`. That still works, but a Discord destination gives you embeds, rate-limit handling and URL checks.
</Callout>

## Slack [#slack]

1. In Slack, create an [incoming webhook](https://api.slack.com/messaging/webhooks) for the channel and copy its URL.
2. **New destination → Slack**, then paste the URL and write the message with the same variables as Discord:

```
*New purchase* {{properties.$product_name}} for {{properties.$robux}} Robux by {{person.properties.name}}
```

Slack accepts about one message a second per webhook, so busy events queue up briefly.

## Telegram [#telegram]

Post to a Telegram channel or group.

1. **New destination → Telegram**. The page shows the RoChart bot's name.
2. Add the bot to your channel or group. In a channel, make it an admin that can post messages.
3. Enter the chat id. A public channel can use its `@name`. Otherwise, forward a message from the chat to [@userinfobot](https://t.me/userinfobot), which replies with the id. Channel and group ids start with `-100`.
4. **Send test**. If the bot isn't in the chat yet, the error says so.

Messages use the same variables as Discord, as plain text. Telegram allows about 20 messages a minute in a group, so busy events queue up.

## Webhook [#webhook]

Send each event as a JSON `POST` to your own backend, a bot, Zapier, Make or n8n. Add headers, such as `Authorization`, if the endpoint needs them.

```json
{
  "uuid": "0193c4f2-…",
  "event": "$purchase",
  "distinct_id": "123456789",
  "person_id": "0190…",
  "timestamp": "2026-11-02T10:00:00.000Z",
  "properties": { "$product_name": "Starter Pack", "$robux": 199, "$product_type": "developer_product" },
  "person": { "properties": { "name": "builderman42", "platform": "Phone", "premium": true } },
  "project": { "id": 1, "name": "Lava Tycoon" },
  "destination": { "id": 3, "name": "Purchases to backend" }
}
```

A `2xx` response counts as delivered. On a `429` or `5xx`, or if the endpoint can't be reached, RoChart tries 3 more times over about a minute. Destinations must be public `https` addresses.

Each request is signed with your project's signing secret (Standard Webhooks headers, with the event's `uuid` as `webhook-id`). See [checking a webhook came from RoChart](/docs/alerts#checking-a-webhook-came-from-rochart).

## Choose the events [#choose-the-events]

- **Which events**: pick one or more events, or leave it empty to send every event.
- **Only when**: filter on event properties, such as `$robux ≥ 1000` or `$product_type = game_pass`.

**Send test** posts a sample event, so you can check the endpoint or the Discord message before saving.

## Watch it work [#watch-it-work]

A destination's page shows how many events it has delivered or failed, the last error, and a log of recent deliveries. Pause one with the switch. Events that arrive while it's paused aren't sent later.

## Agents [#agents]

Agents can set these up over MCP with `create_destination`, `list_destinations`, `get_destination`, `update_destination` and `delete_destination`. For example: *"Post every purchase over 1,000 Robux to our Discord #sales channel with the player's name."* They'll ask you for the webhook URL or chat id.
