Home
Back in Stock

Reports

Five back-in-stock reports, each readable as JSON and exportable as a CSV via an async job.

The back-in-stock reports resource mirrors the preorder reports machinery: every report has a JSON read and a sibling async CSV export. Reads run against read replicas.

Path prefix: /back_in_stock/reports

Report manifest

GET /back_in_stock/reports

Returns the catalog of available reports — { "reports": [{ "name", "description", "aliases", "url" }] } — generated from the live registry so it can't drift from the actual report actions.

Shared filters

All reads accept the same filter set:

ParamTypeNotes
from, toISO 8601 dateWindow — defaults to the last 30 days
variant_idintegerSingle Shopify variant id
product_idintegerSingle Shopify product id
channelemail / sms / push
granularityday / week / monthTime-series reports only; default day

Every response echoes the resolved window at the top: "window": { "from": "...", "to": "..." }.

Reads

MethodPathDescription
GET/back_in_stock/reports/summaryHigh-level overview of back-in-stock activity over a date window.
GET/back_in_stock/reports/signupsTime-series of signups created (day/week/month).
GET/back_in_stock/reports/notificationsTime-series of notifications sent, split by channel.
GET/back_in_stock/reports/conversionsTime-series of orders attributed to alerts and the revenue recovered.
GET/back_in_stock/reports/products_in_demandRanked variants by waitlist demand.

Summary

Totals for the window — waitlist size, sends, and what they recovered:

{
  "window": { "from": "...", "to": "..." },
  "currency": "USD",
  "totals": {
    "signups": 420,
    "pending": 180,
    "notified": 240,
    "conversions": 96,
    "conversion_rate": 0.4,
    "recovered_revenue": "4812.50"
  }
}

conversion_rate is the share of notified signups that converted to an attributed order. Conversions are order attributions at medium/high confidence.

Signups

Series of signups created per bucket, with the pending/notified split:

{
  "window": { "...": "..." },
  "granularity": "day",
  "series": [
    { "date": "2026-06-01", "signups": 24, "pending": 10, "notified": 14 }
  ]
}

Notifications

Series of sends per bucket, split by channel, with blocked counts:

{
  "window": { "...": "..." },
  "granularity": "day",
  "series": [
    { "date": "2026-06-01", "sent": 120, "blocked": 4, "email": 100, "sms": 15, "push": 5 }
  ]
}
Note

This is the aggregate view. For the row-level send log — who got each notification, and why a blocked send was stopped — use the read-only notifications resource at GET /back_in_stock/notifications.

Conversions

Series of attributed orders and recovered revenue per bucket:

{
  "window": { "...": "..." },
  "granularity": "day",
  "currency": "USD",
  "series": [
    { "date": "2026-06-01", "orders": 8, "recovered_revenue": "401.00" }
  ]
}

Products in demand

Ranked variants by demand — the v2 replacement for v1 products_in_demand, and the answer to "what should I restock first?".

ParamNotes
sort_bypending (default) / total / last_requested_at
directiondesc (default) / asc
page, per_pageper_page default 50, max 500
{
  "window": { "...": "..." },
  "sort_by": "pending",
  "direction": "desc",
  "rows": [
    {
      "shopify_variant_id": 123,
      "shopify_product_id": 456,
      "variant_title": "Small / Blue",
      "product_title": "Classic Tee",
      "pending": 42,
      "total": 65,
      "last_requested_at": "2026-06-09T08:30:00Z"
    }
  ],
  "meta": { "total_count": 1, "page": 1, "per_page": 50, "total_pages": 1 }
}

CSV exports

Every read has a sibling */export action:

MethodPath
POST/back_in_stock/reports/summary/export
POST/back_in_stock/reports/signups/export
POST/back_in_stock/reports/notifications/export
POST/back_in_stock/reports/conversions/export
POST/back_in_stock/reports/products_in_demand/export

Exports accept the same filters as the corresponding read (plus sort_by, direction, page, per_page where the read supports them). They submit a job and return 202 Accepted:

{
  "job_id": "exp_1a2b3c4d5e6f7a8b",
  "status_url": "/api/v2/external/back_in_stock/reports/exports/exp_1a2b3c4d5e6f7a8b"
}

Polling

GET /back_in_stock/reports/exports/:job_id

status is one of running, ready, failed, cancelled. When ready, the response includes the download URL:

{
  "job_id": "exp_1a2b3c4d5e6f7a8b",
  "status": "ready",
  "download_url": "https://stoq-exports.s3.amazonaws.com/...",
  "file_name": "back_in_stock_signups_2026-06-10.csv",
  "row_count": 420,
  "progress": 100
}

An unknown job_id returns 404. The polling pattern is the same one used across v2 — see Bulk & Async Jobs.

Example: signups by week, then export

curl 'https://app.stoqapp.com/api/v2/external/back_in_stock/reports/signups?from=2026-03-01&to=2026-06-01&granularity=week' \
  -H "X-Auth-Token: $STOQ_API_KEY"
curl -X POST 'https://app.stoqapp.com/api/v2/external/back_in_stock/reports/signups/export' \
  -H "X-Auth-Token: $STOQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from":"2026-03-01","to":"2026-06-01","granularity":"week"}'
# Poll until status: "ready"
curl 'https://app.stoqapp.com/api/v2/external/back_in_stock/reports/exports/exp_1a2b3c4d5e6f7a8b' \
  -H "X-Auth-Token: $STOQ_API_KEY"