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

# Custom events

> Reference for the custom events used to record custom user actions and in-app behaviour.

Record any [custom events](/features/product-analytics/custom-events) in your app,
along with properties that describe the action.

Custom events can capture a broad range of actions, such as starting a swap or completing a deposit.

Additional information about the event can be included in the properties field. For example, for a `Swap Completed` event, you may want to include the token pair, input amount, and output amount.

## Naming events

When naming events, Formo recommends establishing a consistent naming convention that uses:

* Consistent formatting: Event names are case sensitive.
* A consistent syntax:  Adopt nouns and past tense verbs like `Swap Completed` and `Deposit Submitted`. A standard of `[Noun] + [Past-Tense Verb]` ensures all your events are consistent.
* A consistent actor: Does `Transaction Submitted` mean that the user submitted a transaction or that your app submitted it on their behalf? If all your events are named in a way that reflects the user's perspective, the meaning is clear immediately.

This allows everyone including you 6 months from now to instantly understand the meaning of an event.

## Properties

Properties are additional information that give more clarity of your users' actions.

Every custom event has `type` set to `track` and `event` set to your custom event name, with the event-specific data passed in the `properties` object:

```json theme={null}
{
  "type": "track",
  "event": "Swap Completed",
  "properties": {
    "pair": "ETH/USDC",
    "token_in": "ETH",
    "token_out": "USDC"
  }
}
```

Formo has reserved some standard properties listed in the following table and handles them in a special manner.

### Tracking volume, revenue, points

You can track `volume`, `revenue`, and `points` in your events.
Once tracked, they are shown on the dashboard.

Include these optional properties in a custom event to track values associated with an action.

| Property   | Type   | Description                                                                                                                                                                      |
| :--------- | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `volume`   | Number | The volume amount as a result of an event. For e.g., a token swap worth \$20.00 would result in a volume of 20.00. Can be positive or negative e.g. send -100 to track outflows. |
| `revenue`  | Number | The revenue amount as a result of an event. For e.g., a transaction with a protocol fee of \$5.00 would result in a revenue of 5.00. Must be a non-negative number.              |
| `currency` | String | The currency of the revenue as a result of the event, set in ISO 4217 format. If this is not set, Formo assumes the revenue is in USD.                                           |
| `points`   | Number | An abstract value such as points or XP associated with an event, to be used by various teams.                                                                                    |

<Frame caption="Revenue tracking.">
  <img src="https://mintcdn.com/formo/Qbe3dL6juMIXAS6y/images/revenue.png?fit=max&auto=format&n=Qbe3dL6juMIXAS6y&q=85&s=ecb4b648521276d5b03836f16ce806a2" alt="Revenue tracking." width="703" height="394" data-path="images/revenue.png" />
</Frame>

For example, call `.track()` with the reserved properties alongside any other event properties:

```typescript theme={null}
analytics.track('Swap Completed', {
  pair: 'ETH/USDC',
  token_in: 'ETH',
  token_out: 'USDC',
  amount_in: 1.5,
  amount_out: 4500,
  volume: 4500,
  revenue: 13.5,
  points: 50
});
```

### Deduplicating events

The Web and Mobile SDKs deduplicate custom events in two ways.

**Automatically, for 60 seconds.** When `track()` is called twice within 60 seconds with the same event name, properties, and context, for the same wallet and user, the SDK sends the event once. This handles accidental double-fires, such as a React effect that runs twice. It applies within one page or app session.

**With an idempotency key, for retries.** For business-critical events, add the reserved `idempotency_key` property with a stable identifier for the action, such as an order ID. Every call that reuses the key for the same event name gets the same message ID, so ingestion keeps one event however many times it is sent, including across page reloads and app restarts.

| Property          | Type              | Description                                                                                                                   |
| :---------------- | :---------------- | :---------------------------------------------------------------------------------------------------------------------------- |
| `idempotency_key` | String or Integer | A stable identifier for one action, such as an order ID. The key is hashed into the message ID and is not sent as a property. |

```typescript theme={null}
analytics.track('Order Placed', {
  market: 'ETH-USDC',
  side: 'buy',
  volume: 2500,
  idempotency_key: order.id,
});
```

#### Delivery callback

`track()` accepts a callback as its fourth argument. It runs once the SDK's delivery attempt for that event settles:

```typescript theme={null}
analytics.track('Order Placed', properties, undefined, (error) => {
  if (error) console.error('Event delivery failed', error);
});
```

A callback without an error means the events API accepted the batch, not that downstream processing has finished. A call the SDK recognises as a duplicate is never sent and does not invoke its callback.

## Sample Payload

```json theme={null}
{
  "type": "track",
  "event": "Swap Completed",
  "properties": {
    "pair": "ETH/USDC",
    "token_in": "ETH",
    "token_out": "USDC",
    "amount_in": 1.5,
    "amount_out": 4500,
    "volume": 4500,
    "revenue": 13.5,
    "points": 50
  }
}
```
