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

# Monitor a store for price changes

Track a Shopify store for daily price changes — set up monitoring, configure frequency, react to webhooks, and query the changelog.

## Overview

This guide walks through the full price-monitoring flow: track a store, set its scan frequency, receive webhook callbacks on every new snapshot, and query the changelog API for per-product diffs. By the end you'll have a working pipeline that pages you (or your agent) whenever a tracked store changes pricing.

**Prerequisites:**
- A [ShopSniffer account](https://shopsniffer.com/sign-in) (Pro Monthly recommended for monitoring)
- An [API key](https://shopsniffer.com/dashboard)
- A webhook endpoint reachable from the public internet

## The pipeline

graph LR
    A[POST /api/shops] --> B[PATCH frequency= daily]
    B --> C[Daily 12 UTCMonitorShopsWorkflow]
    C --> D[New snapshot job]
    D --> E[Changelogdiff computed]
    E --> F[Webhook POSTto your URL]
    F --> G[Your handlerfetches changes]
    G --> H[React: Slack,DB, email, etc.]

## Step 1 — track the store

```bash
curl -X POST https://shopsniffer.com/api/shops \
  -H "X-API-Key: ss_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"domain": "allbirds.com"}'
```

You'll get back a `shop` object with the slug. Save the slug — you'll use it in subsequent calls.

## Step 2 — set daily frequency

By default, shops are tracked weekly. For price monitoring, you almost certainly want daily:

```bash
curl -X PATCH https://shopsniffer.com/api/store-detail/allbirds-com/frequency \
  -H "X-API-Key: ss_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"frequency": "daily"}'
```

The effective frequency is the most aggressive across all subscribers to this store — if any user sets daily, everyone gets daily. See [monitoring workflows](/monitoring-workflows#effective-frequency) for the rules.

## Step 3 — wire up a webhook endpoint

Create a small server that receives `job.completed` events and calls the changelog API to fetch diffs:

**Hono (Cloudflare Workers)**

```typescript
    import { Hono } from "hono";

    const app = new Hono();

    app.post("/webhook/shopsniffer", async (c) => {
      const body = await c.req.json();

      if (body.event !== "job.completed") {
        return c.json({ ok: true });
      }

      // Fetch the per-job changelog
      const changes = await fetch(
        `https://shopsniffer.com/api/changelog/${body.job_id}`,
      ).then((r) => r.json());

      // Filter to price changes only
      const priceChanges = changes.changes?.flatMap((entry) =>
        entry.details?.filter((d) => d.type === "price_change") ?? [],
      ) ?? [];

      if (priceChanges.length > 0) {
        console.log(`${priceChanges.length} price changes on ${body.domain}`);
        // Send to Slack, Discord, DB, email, etc.
        await notifySlack(body.domain, priceChanges);
      }

      return c.json({ ok: true });
    });

    export default app;
    ```
  **Express (Node.js)**

```javascript
    import express from "express";
    const app = express();
    app.use(express.json());

    app.post("/webhook/shopsniffer", async (req, res) => {
      const { event, job_id, domain } = req.body;

      if (event !== "job.completed") {
        return res.json({ ok: true });
      }

      const changes = await fetch(
        `https://shopsniffer.com/api/changelog/${job_id}`,
      ).then((r) => r.json());

      const priceChanges = (changes.changes ?? [])
        .flatMap((e) => e.details ?? [])
        .filter((d) => d.type === "price_change");

      if (priceChanges.length > 0) {
        console.log(`${priceChanges.length} price changes on ${domain}`);
        await notifySlack(domain, priceChanges);
      }

      res.json({ ok: true });
    });

    app.listen(3000);
    ```
  **FastAPI (Python)**

```python
    from fastapi import FastAPI
    import httpx

    app = FastAPI()

    @app.post("/webhook/shopsniffer")
    async def webhook(body: dict):
        if body.get("event") != "job.completed":
            return {"ok": True}

        async with httpx.AsyncClient() as client:
            r = await client.get(
                f"https://shopsniffer.com/api/changelog/{body['job_id']}"
            )
            changes = r.json()

        price_changes = [
            d
            for entry in changes.get("changes", [])
            for d in entry.get("details", [])
            if d.get("type") == "price_change"
        ]

        if price_changes:
            print(f"{len(price_changes)} price changes on {body['domain']}")
            await notify_slack(body["domain"], price_changes)

        return {"ok": True}
    ```

**Secure your endpoint**

Webhooks are **unsigned** — ShopSniffer does not sign callbacks with HMAC. Protect your endpoint by:
  - Using a hard-to-guess path (e.g. `/webhook/shopsniffer/<random-token>`)
  - Validating the `job_id` against a job you created and stored locally
  - Allowlisting Cloudflare Workers egress IPs on your firewall

  Because delivery is single-attempt (no retries), also build a fallback poll that reconciles any missed events.

## Step 4 — register the webhook URL on the next scan

Webhooks are per-job, not per-store. To receive callbacks on every daily scan, register a callback URL when *creating* a job for that store — the easiest way is to create an "initial" job with the webhook and let the monitoring workflow inherit the pattern. Or, more reliably, create a small reconciliation cron on your side that polls once a day if no webhook arrived.

For an always-reliable pipeline, combine webhooks with a safety-net poll: when you create any job, register the webhook *and* schedule a timer that fires in 15 minutes. If the webhook hasn't arrived, poll `GET /store-detail/:slug/changes?page=1&limit=1` to see if the daily scan produced new changes.

## Step 5 — react to price changes

Once you have price diffs flowing into your handler, the downstream options are yours:

- **Slack / Discord / Teams** — paste the diff into a channel
- **Database** — append to a `price_history` table for trend charts
- **Email digest** — batch daily and send one summary per recipient
- **Agent trigger** — kick off an LLM prompt to analyze the diff ("are these typical seasonal price moves?")
- **Shopify sync** — auto-match a competitor's new sale price in your own store

## Querying the changelog directly

Even without webhooks, you can query the changelog at any time:

```bash
# Get the 50 most recent changes for a store
curl "https://shopsniffer.com/api/store-detail/allbirds-com/changes?page=1&limit=50" \
  -H "X-API-Key: ss_your_key_here"

# Filter to only product changes
curl "https://shopsniffer.com/api/store-detail/allbirds-com/changes?item_type=product" \
  -H "X-API-Key: ss_your_key_here"
```

See the [monitoring API](/api/monitoring) for the full set of monitoring endpoints, including snapshot history, compare, analytics, and inventory events.

## Next steps

**[Monitoring API](/api/monitoring)**

Full reference for all `/api/store-detail/*` endpoints.
  **[Webhooks](/webhooks)**

Delivery semantics, payload shape, security.
  **[Monitoring workflows](/monitoring-workflows)**

Background cron schedule and effective-frequency rules.
  **[AI agent integration](/agent-integration)**

Wire monitoring into an autonomous agent loop.