[relaybell docs › Automation › Events & automation](https://relaybell.com/docs/events-automation) · [All docs as Markdown](https://relaybell.com/docs.md) · [Index for AI assistants](https://relaybell.com/llms.txt)

# Events & automation

> POST /api/v1/events, conversions and attribution, automations (conditions, delays, cancelOn) and auto-capture.

**POST** `/api/v1/events` — send a behavioral event from your server (scope `events:write`; the browser SDK's `track()` uses a public route instead). Name the person with `externalId` (your user id) or `subscriberId` (relaybell's id); an event that names nobody is stored but only a [product alert](https://relaybell.com/docs/product-alerts) can come of it.

```json
{ "event": "purchase", "externalId": "user_123", "properties": { "value": 79.99, "orderId": "A123" } }
```

It answers `202` (`400 event_required` without a name; `name` works as another spelling of `event`; names are cut to 120 characters):

```json
{ "ok": true, "eventId": "1234", "subscriberId": "…", "isConversion": true,
  "attribution": { "notificationId": "5b0c2f7e-…", "value": 79.99 },
  "automationsTriggered": 0, "sequencesEnrolled": 0 }
```

**Conversions:** `purchase`, `order.completed` and `checkout.completed` count as conversions (or the list in your `conversionEvents` setting), and so does any other event with a numeric `value` or `revenue` property — except the funnel steps `cart.updated` and `checkout.started`. A conversion is credited, with its value, to the last notification delivered to that subscriber in the previous 7 days (`attributionWindowDays` setting). `GET /api/v1/events` (scope `subscribers:read`) lists the latest 100 events.

## Automations {#automations}

`/api/v1/automations` — one push to a person when they do something: an event arrives, the optional delay passes, the message goes out. This powers cart abandonment (a `purchase` cancels the reminder), back-in-stock and price-drop (see [Product alerts](https://relaybell.com/docs/product-alerts)), and welcome-back flows. Each event fires every enabled automation with that `event` name whose `conditions` match.

```http
POST /api/v1/automations
{ "name": "Cart abandon", "event": "cart.updated", "delaySeconds": 3600, "cancelOn": "purchase",
  "conditions": [ { "field": "cartTotal", "op": "gte", "value": 20 } ],
  "action": { "title": "You left {product|something} in your cart 🛒", "body": "Complete your order before it's gone.", "url": "{url}" } }
```

| Field | Description |
| --- | --- |
| `event` \* | The event name that starts it (`400 event_required`). |
| `action` \* | `{ title, body, url, icon }` — `title` is required (`400 action_title_required`). Merge tags take values from the triggering event's properties: `{product}`, `{url}`, `{price\|}`, … |
| `name` | Up to 120 characters (default `"Automation"`). |
| `delaySeconds` | Wait before sending (default 0). With a delay, a newer event from the same person **replaces** their pending one, so three cart updates make one reminder, timed from the last. |
| `cancelOn` | An event name that cancels the person's pending (delayed) push, e.g. `purchase`. |
| `conditions` | A list of `{ field, op, value }` checked against the event's **properties**; all must match. `op`: `eq` (default), `neq`, `contains` (case-sensitive), `exists`, and the number comparisons `gt`, `gte`, `lt`, `lte`. Empty or left out: every event matches. |
| `oncePerSubscriber` | `true` = at most once per person, ever (a pending or sent one blocks the next). |
| `enabled` | Default `true`; `false` pauses it (pending pushes are dropped when they come due). |

`PUT /api/v1/automations/:id` replaces the automation with the body you send — `enabled` goes back to `true` unless you send `false` — except `conditions`, which are kept when the body has no `conditions` key. `GET /api/v1/automations` lists them with `sent` (all-time deliveries) and `stats30d` `{ sent, clicks, ctr, conversions, revenue }`; `GET /api/v1/automations/:id` returns one; `DELETE` removes one. Writes need `settings:write`. Automation pushes follow your quiet hours and frequency cap, and never go to someone who has unsubscribed.

## Auto-capture & Google Tag Manager {#auto-capture}

Rather than call `track()` by hand everywhere, opt in and relaybell records pageviews, declarative clicks and scroll depth for you — only for subscribed or identified visitors, so anonymous traffic makes no noise. The events are `page_view` `{ url, path, title }` (also on single-page-app route changes), your own `data-rb-event` names, and `scroll_depth` `{ percent }` at 25, 50, 75 and 100%:

```js
RelayBell.init({ projectId: "YOUR_PROJECT_ID", apiBase: "https://relaybell.com", autoCapture: true });
// granular: autoCapture: { pageviews: true, clicks: true, scroll: true }

// Track any click declaratively — the other data-rb-* attributes become event properties
// (data-rb-sku → sku, data-rb-unit-price → unit_price; values are text):
<button data-rb-event="add_to_cart" data-rb-sku="TEE-01" data-rb-price="19.99">Add to cart</button>
```

**Mirror events from Google Tag Manager / GA.** You can't trigger push *directly* from Google Analytics or Meta Pixel — those are analytics and ad tools, not real-time per-user event buses, and their terms don't allow using that data to message individuals. Instead, fire the same events you already track into relaybell too, most easily via a GTM Custom HTML tag on your existing dataLayer events, then segment and automate on them here:

```html
<script>/* GTM Custom HTML tag, fired on your event trigger.
   {{Event}} is GTM's built-in variable holding the dataLayer event name. */
if (window.RelayBell) RelayBell.track({{Event}}, {});</script>
```

To send details with the event, replace `{}` with an object built from your own Data Layer Variables, for example `{ productId: {{DLV - productId}} }`.
