---
title: "Daily sampling matrix by product"
method: POST
path: "/samples/daily-by-product"
tags: ["Samples"]
---

# Daily sampling matrix by product

`POST /samples/daily-by-product`

Day-by-day sampling for every product at once: one row per product, one column per day, and a Requested / Approved / Shipped count in each cell. The same matrix the portal's Sample funnel tab renders, from the same query layer.

**Days are the shop's own calendar days**, not UTC — a request made at 9pm in the shop's timezone belongs to that day. Set `x-shop-id` to a single shop; `all` or a comma-separated list is rejected, because two shops in different regions do not share a day axis.

**`requested` and `shipped` are TikTok's own daily counts** — the numbers Seller Center and the portal's Sample requests and Samples shipped cards show — whenever Seller Center data covers the window. They include samples TikTok's sample-request list never shows, so they can be higher than a per-request export. `approved` counts every TikTok sample application on its own and equals the portal's Samples approved card. `stage_sources` says which source served each stage.

`requested` counts requests MADE that day; `approved` and `shipped` count the approvals and shipments that HAPPENED that day, whatever day the request was made — so a product can show approvals on a day it received no requests, and the columns are not a funnel of one another.

`start_date` and `end_date` are required; a window longer than 92 days keeps its most recent 92 days (`clamped_to_days` says so, and `date_range` echoes what was served). Pagination is over PRODUCTS; `totals` always covers every in-scope product, not just the page.

`requested` is dated by TikTok's own request time, so history from before the shop joined lands on its real days. Approvals, and shipments counted from applications, recorded during a shop's first CRM sync are excluded, so onboarding day does not read as a spike of real activity.

## Request body

- SamplesDailyByProductRequest — POST /samples/daily-by-product request body (CORE-7171). Dates are REQUIRED: a per-day matrix over an unbounded window is not a meaningful surface, and the dashboard refuses it for the same reason. Windows longer than 92 days keep their LAST 92 days (the response says so in ``clamped_to_days`` and echoes the served window in ``date_range``).
  - `start_date` string, date, required — First day of the window (inclusive).
  - `end_date` string, date, required — Last day of the window (inclusive).
  - `product_ids` string[], nullable — Restrict the matrix to these products. Omit for every product with sampling activity in the window.
  - `page` integer — Page over PRODUCTS (rows), not days.
  - `page_size` integer

## Response `200`

Successful Response

- SamplesDailyByProductResponse — Products × days sampling matrix for one shop (CORE-7171). Same read as the portal's Sample-funnel matrix: days are the SHOP's calendar days, not UTC's, and the stage semantics differ on purpose — ``requested`` counts the day's request cohort while ``approved`` / ``shipped`` count the events that happened that day.
  - `data` SampleDailyProductItem[], required
    - `product_id` string, required
    - `product_name` string, nullable — Catalog name; falls back to the product_id when the catalog has no row.
    - `requested` integer[] — Requests MADE on each day (the day's request cohort).
    - `approved` integer[] — Approvals that HAPPENED on each day, whatever day the request was made.
    - `shipped` integer[] — Shipments that HAPPENED on each day, whatever day the request was made.
    - `totals` SampleDailyStageTotals, required — One product's window totals, per stage.
      - `requested` integer
      - `approved` integer
      - `shipped` integer
  - `dates` string[], required — The day axis, YYYY-MM-DD, oldest first, in the shop's own timezone. Every per-day array aligns with it.
  - `totals` object, required — Per-day totals across EVERY in-scope product (not just this page), keyed by stage.
  - `pagination` PublicApiCorePaginationPaginationMeta, required — Pagination metadata returned in responses.
    - `total_count` integer, required
    - `page` integer, required
    - `page_size` integer, required
    - `total_pages` integer, required
  - `date_range` DateRange
    - `start_date` string, nullable
    - `end_date` string, nullable
  - `clamped_to_days` integer, nullable — Set to 92 when a longer window was clamped to its last 92 days; null otherwise.
  - `stage_sources` SampleDailyStageSources — Where each stage's counts came from. ``seller_center`` is TikTok's own daily count, the number Seller Center and the portal's Sample requests / Samples shipped cards show. ``applications`` counts TikTok sample applications one by one; ``funnel_events`` is its fallback.
    - `requested` 'seller_center' | 'applications' | 'funnel_events', required
    - `approved` 'seller_center' | 'applications' | 'funnel_events', required
    - `shipped` 'seller_center' | 'applications' | 'funnel_events', required

## Other responses

- `422` — Validation Error

## Changes

> 29 revisions in range; 1 not diffed.

- **2026-10-02** `11512e6b7f67` — 1 info
  - added the optional property `stage_sources` to the response with the `200` status
- **2026-09-24** `8640294d0f3d` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/reacherapp/apis/reacher-data-api/changes/samples/daily-by-product/post.md)

---

[API](https://skmtc.dev/reacherapp/apis/reacher-data-api.md) · [All operations](https://skmtc.dev/reacherapp/apis/reacher-data-api/llms.txt) · [OpenAPI document](https://skmtc.dev/reacherapp/apis/reacher-data-api/revisions/881910053958?raw)
