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

# Custom Events & Goals

> Track user behavior and conversion goals to build RFM segments and enrich contact timelines.

## Overview

Custom events track user actions from external systems (e-commerce, CRM, analytics). They appear in the contact timeline and enable goal-based segmentation for building RFM (Recency, Frequency, Monetary) segments.

## Contact Timeline

Every custom event creates an entry in the contact timeline, giving you a complete history of user interactions:

<img src="https://mintcdn.com/notifuse/tEno5RTKyhyoJvpR/assets/screenshots/custom_event_timeline.png?fit=max&auto=format&n=tEno5RTKyhyoJvpR&q=85&s=c81bda6cfa2f97f10a84bba513bc8de2" alt="Contact timeline with custom events" width="3434" height="1598" data-path="assets/screenshots/custom_event_timeline.png" />

Events display with their `event_name` as the timeline entry type (e.g., "order.completed", "subscription.started"). The `properties` field stores additional context visible in the timeline details.

## Goal Types

Goals are the key to building powerful segments. Each event can optionally track a goal:

| Type | Use Case | goal\_value |
| - | - | - |
| `purchase` | E-commerce orders, one-time payments | Required |
| `subscription` | SaaS subscriptions, memberships | Required |
| `lead` | Contact forms, demo requests | Optional |
| `signup` | Account registrations | Optional |
| `booking` | Appointments, consultations | Optional |
| `trial` | Free trial activations | Optional |
| `other` | Custom conversion events | Optional |

## Building RFM Segments

RFM (Recency, Frequency, Monetary) analysis identifies your best customers. Use the segment builder with "Custom Events Goals" conditions:

<img src="https://mintcdn.com/notifuse/tEno5RTKyhyoJvpR/assets/screenshots/segment_goals.png?fit=max&auto=format&n=tEno5RTKyhyoJvpR&q=85&s=39e224950863e4a9c4d61785b10386cd" alt="Segment builder with custom events goal" width="2470" height="1308" data-path="assets/screenshots/segment_goals.png" />

### Monetary: Total Spend

Select goal type "purchase", aggregate "sum", and set a minimum value to find high-spending customers.

### Frequency: Purchase Count

Select goal type "purchase", aggregate "count", and set a minimum number to find repeat buyers.

### Recency: Recent Activity

Use the timeframe filter "in the last X days" to find customers with recent purchases.

### Example RFM Segments

* **VIP Customers**: Spent \$1000+ AND 5+ orders AND purchased in last 60 days
* **At-Risk High-Value**: Spent \$500+ AND no purchase in 90 days
* **New High-Potential**: First purchase over \$200 in last 30 days

### Aggregate Options

| Option | Description |
| - | - |
| Sum | Total of all goal values (lifetime spend) |
| Count | Number of events (order count) |
| Average | Average goal value (AOV) |
| Min | Smallest goal value |
| Max | Largest goal value |

### Timeframe Options

| Option | Description |
| - | - |
| Anytime | All historical data |
| In the last X days | Rolling window |
| In date range | Specific period |
| Before date | Historical cutoff |
| After date | Since a specific date |

## Tracking Events

### Single Event

```bash theme={null}
curl -X POST "https://your-instance.com/api/customEvents.upsert" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace_id": "your_workspace",
    "email": "customer@example.com",
    "event_name": "order.completed",
    "external_id": "order_12345",
    "properties": {
      "items": ["Product A", "Product B"],
      "currency": "USD"
    },
    "goal_type": "purchase",
    "goal_value": 149.99
  }'
```

### Batch Import

Import up to 50 events at once:

```bash theme={null}
curl -X POST "https://your-instance.com/api/customEvents.import" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace_id": "your_workspace",
    "events": [
      {
        "email": "user1@example.com",
        "event_name": "order.completed",
        "external_id": "order_001",
        "goal_type": "purchase",
        "goal_value": 99.99
      },
      {
        "email": "user2@example.com",
        "event_name": "order.completed",
        "external_id": "order_002",
        "goal_type": "purchase",
        "goal_value": 249.99
      }
    ]
  }'
```

## Event Name Format

Event names must be lowercase using letters, numbers, dots, underscores, hyphens, or slashes:

* `order.completed`
* `subscription/started`
* `form_submit`
* `user-signup`

<Note>
  Goals recorded by [Web Analytics](/web-analytics/goals) reach the contact timeline through a narrower rule: ASCII letters, numbers, dots and slashes only, with everything else — hyphens and accented characters included — replaced by an underscore. A web goal named `user-signup` arrives as `user_signup`. If you send the same event through both paths, pick a name that survives each.
</Note>

## Handling Refunds

Use negative `goal_value` for refunds to keep accurate LTV calculations:

```json theme={null}
{
  "email": "customer@example.com",
  "event_name": "order.refunded",
  "external_id": "refund_12345",
  "goal_type": "purchase",
  "goal_value": -50.00
}
```

## Permissions

Custom events have no permission of their own: they answer to **Contacts**. Recording or importing an event needs Contacts **write**, and reading one needs Contacts **read** — so granting Contacts read also opens the events recorded against your contacts, and the contact timeline they feed. See [Admins & API Keys](/features/admins-api-keys).

## Notes

* Contacts are auto-created if they don't exist
* Events update only if `occurred_at` is newer than existing
* Soft-delete by setting `deleted_at` (set to `null` to restore)
* See [API Reference](/api-reference/import-custom-events) for complete endpoint documentation


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.