[relaybell docs › Automation › Product alerts](https://relaybell.com/docs/product-alerts) · [All docs as Markdown](https://relaybell.com/docs.md) · [Index for AI assistants](https://relaybell.com/llms.txt)

# Product alerts

> Back-in-stock and price-drop alerts: the "Notify me" button, interest events and the broadcast to everyone interested.

A product alert is about a *product*, not one person: your server reports that a product is back in stock or cheaper, and relaybell sends it to everyone who showed interest in that product. It takes two parts.

**1. Record interest from the browser.** Call `track()` with the product's id while the visitor is subscribed — interest is only stored for a browser with a push subscription, so check `isSubscribed()` (or subscribe first):

```js
// "Notify me when back in stock" on a sold-out product.
// For a variable product, send the selected VARIATION's id.
notifyButton.addEventListener("click", async () => {
  const r = await RelayBell.subscribeNow();               // must run from the click
  if (r.subscribed) RelayBell.track("waitlist.join", { productId: "1234" });
});

// Product page view (counts toward price-drop alerts)
RelayBell.isSubscribed().then((yes) => {
  if (yes) RelayBell.track("product.viewed", { productId: "1234" });
});

// Added to cart (counts toward price-drop alerts only for a subscribed browser)
RelayBell.track("cart.updated", { productId: "1234", product: "Blue Shoes", price: 49, url: location.href });
```

**2. Report the product event from your server** (API key). Send `back_in_stock` or `price_drop` to `POST /api/v1/events` with **no** `externalId` (and no `subscriberId` or `endpoint`) — an event that names a person is treated as that person's own event and is not broadcast. Only this authenticated route can broadcast; the browser's `track()` never does.

```bash
curl -X POST https://relaybell.com/api/v1/events \
  -H "Authorization: Bearer rb_live_xxxxx" -H "Content-Type: application/json" \
  -d '{ "event": "price_drop",
        "properties": { "productId": "1234-42", "parentId": "1234", "product": "Blue Shoes",
                        "url": "https://shop.example/p/blue-shoes", "oldPrice": 59, "newPrice": 49 } }'
```

| Property | Description |
| --- | --- |
| `productId` \* | The product (or variation) that changed. Use the same id you send from the browser. Ids are up to 64 letters, digits and `_ . : -` — anything else is ignored. |
| `parentId` | For a variation, its parent product — interest recorded on either id counts. |
| `product`, `url`, `oldPrice`, `newPrice` | Optional details for your message, used as `{product}`, `{url}`, `{oldPrice}`, `{newPrice}` (any other property works as a placeholder too, e.g. `{percentOff}`). |

Nothing is sent unless you have an **enabled automation** for that event (Automations → New automation → **Back in stock** / **Price drop**, or `POST /api/v1/automations` with `"event": "back_in_stock"` or `"price_drop"`). Its title, body and url are templates, e.g. `"{product|This item} is back!"`. Who gets it:

- **`back_in_stock`** — everyone on the product's waitlist (`waitlist.join` for `productId` or `parentId`). The waitlist is **consumed**: each person is taken off it as the alert goes out, so they get one alert per join and a later restock doesn't alert them again. It is sent as a transactional message, so the daily frequency cap can't drop an alert someone asked for.
- **`price_drop`** — anyone on the waitlist, plus subscribers who viewed (`product.viewed`) or carted (`cart.updated`) the product in the last 30 days. This list is **not** consumed. Price drops are **deduplicated**: at most one `price_drop` alert per product (keyed by `parentId`, else `productId`) every 10 minutes — repeats inside that window send nothing.

For product alerts, `cancelOn` and `oncePerSubscriber` don't apply; `conditions` (checked against the event's properties), `delaySeconds` and your quiet hours still do. Unsubscribed people are never included, and one alert reaches at most 100,000 people. The `202` answer adds `productAlerts` (alerts created) and `alertAudience` (people reached).
