---
title: "One stored promotion with its products, SKUs and prices"
method: GET
path: "/shop-promotions/{promotion_kind}/{promotion_id}"
tags: ["Promotion Snapshots"]
---

# One stored promotion with its products, SKUs and prices

`GET /shop-promotions/{promotion_kind}/{promotion_id}`

## Request

- Base URL: `https://api.reacherapp.com/public/v1`
- URL: `https://api.reacherapp.com/public/v1/shop-promotions/{promotion_kind}/{promotion_id}`
- Auth: API key in header `x-api-key` and API key in header `x-shop-id`

A promotion from the newest stored snapshot: its terms, the products and SKUs it lists (up to 1,000; items_truncated says when there are more) with TikTok's promotion price or percent, the SKU's catalog sale_price, and an estimated live price. Pass the shop with x-shop-id; if the id exists in more than one of the requested shops the answer is 409 naming them.

## Path parameters

- `promotion_kind` 'activity' | 'coupon', required: activity or coupon
- `promotion_id` string, required

## Query parameters

- `snapshot` string, nullable: The snapshot token from page 1, to read later pages from the same runs (valid 24 h).

## Response `200`

Successful Response

- PromotionDetailResponse
  - `data` PromotionDetail, required
    - `shop_id` integer, required
    - `promotion_kind` 'activity' | 'coupon', required
    - `promotion_id` string, required
    - `title` string, required
    - `tool_type` 'product_discount' | 'flash_sale' | 'shipping_discount' | 'buy_more_save_more' | 'buy_x_get_y' | 'gift_with_purchase' | 'voucher' | 'unknown', required
    - `tiktok_type` string, required: TikTok's own activity_type, or COUPON.
    - `status` 'upcoming' | 'ongoing' | 'ended' | 'deactivated' | 'unknown', required: As of served_snapshot_at. A promotion whose end_at has passed since then can still read ongoing until the next snapshot; compare end_at with the current time.
    - `tiktok_status` string, required
    - `deal_summary` string, required: One line describing the deal, built from the stored fields.
    - `start_at` string, nullable
    - `start_at_shop_tz` string, nullable
    - `end_at` string, nullable: Scheduled end; an indefinite promotion ends about 100 years out.
    - `end_at_shop_tz` string, nullable
    - `ended_at` string, nullable: When it actually stopped: end_at once expired, the last update when deactivated.
    - `ended_at_shop_tz` string, nullable
    - `is_indefinite` boolean
    - `scope` 'shop_wide' | 'products' | 'unknown', required
    - `product_count` integer, nullable
    - `sku_count` integer, nullable
    - `currency` string, nullable
    - `discount_type` string, nullable
    - `discount_amount` string, nullable
    - `discount_percent` string, nullable
    - `max_discount` string, nullable
    - `min_spend` string, nullable
    - `promo_code` string, nullable
    - `coupon_display_type` string, nullable
    - `claim_start_at` string, nullable
    - `claim_end_at` string, nullable
    - `redemption_start_at` string, nullable
    - `redemption_end_at` string, nullable
    - `claimed_count` integer, nullable
    - `redeemed_count` integer, nullable
    - `detail_status` string, required: ok | carried | not_needed | pending | denied | products_dropped | failed
    - `updated_at` string, nullable
    - `items` PromotionItem[]
      - `product_id` string, required
      - `sku_id` string, nullable: Null when the promotion lists the product, not its SKUs.
      - `item_grain` 'sku' | 'product', required
      - `item_role` 'product' | 'benefit' | 'excluded', required
      - `promo_price` string, nullable: TikTok's promotion price; null for percent-off items.
      - `promo_currency` string, nullable
      - `discount_percent` string, nullable
      - `quantity_limit` integer, nullable: -1 means no limit.
      - `quantity_per_user` integer, nullable
      - `used_quantity` integer, nullable
      - `sale_price` string, nullable: Catalog price before any discount (Get Product), for SKU items. A product item (sku_id null) has none: read its SKUs with promotion_sku_prices and product_id.
      - `sale_price_observed_at` string, nullable
      - `currency` string, nullable
      - `estimated_promo_price` EstimatedPrice: A derived live price: TikTok returns no post-promotion price, so this is computed, never observed.
        - `amount` string, nullable: Decimal string; null when no estimate can be made (see reason).
        - `currency` string, nullable
        - `method` 'promo_price' | 'percent_off', nullable
        - `basis_promotion_id` string, nullable
        - `basis_promotion_kind` 'activity' | 'coupon', nullable
        - `reason` string, nullable: Why amount is null: no_ongoing_product_promotion, unresolved_promotion_details, prices_not_in_run, sku_not_in_prices, currency_mismatch, no_sale_price, quantity_exhausted, or invalid_discount.
        - `unresolved_promotion_ids` string[]
        - `flags` string[]: e.g. quantity_exhausted on a skipped promotion, or quantity_used_unknown when a promotion with a stock limit did not report how much was used (the offer may already be gone).
        - `assumptions` string[]
        - `as_of` string, nullable: The promotions snapshot time the estimate reflects (UTC).
    - `items_truncated` boolean
    - `detail_fetched_at` string, nullable
  - `coverage` ShopCoverage[], required
    - `shop_id` integer, required
    - `shop_timezone` string, required: IANA zone used for the *_shop_tz fields.
    - `promotions` DatasetCoverage, required: Which stored snapshot answered for one dataset of one shop, and how complete and fresh it is.
      - `state` 'collected' | 'degraded' | 'stale' | 'scope_missing' | 'not_collected', required: collected: a complete snapshot under 13 h old. degraded: served, but details are pending, a history branch is capped, or the newest refresh failed. stale: the newest servable snapshot is over 13 h old, or none is under 14 days (then nothing is served). scope_missing: the shop's grants cannot read this data. not_collected: no snapshot exists for this shop.
      - `reason` string, nullable: Why the state is not collected, e.g. no_grant_with_scope.
      - `run_id` string, nullable: The sync run whose snapshot was served.
      - `served_snapshot_at` string, nullable: When the served snapshot was taken (UTC).
      - `last_success_at` string, nullable: When this dataset last refreshed cleanly (UTC).
      - `history_gaps` string[]: Ended-promotion branches TikTok's 10,000-row depth cap hid, e.g. EXPIRED:FLASHSALE.
      - `unresolved_details` integer: Ongoing product-scoped promotions whose product list could not be read in the served snapshot.
    - `prices` DatasetCoverage, required: Which stored snapshot answered for one dataset of one shop, and how complete and fresh it is.
      - `state` 'collected' | 'degraded' | 'stale' | 'scope_missing' | 'not_collected', required: collected: a complete snapshot under 13 h old. degraded: served, but details are pending, a history branch is capped, or the newest refresh failed. stale: the newest servable snapshot is over 13 h old, or none is under 14 days (then nothing is served). scope_missing: the shop's grants cannot read this data. not_collected: no snapshot exists for this shop.
      - `reason` string, nullable: Why the state is not collected, e.g. no_grant_with_scope.
      - `run_id` string, nullable: The sync run whose snapshot was served.
      - `served_snapshot_at` string, nullable: When the served snapshot was taken (UTC).
      - `last_success_at` string, nullable: When this dataset last refreshed cleanly (UTC).
      - `history_gaps` string[]: Ended-promotion branches TikTok's 10,000-row depth cap hid, e.g. EXPIRED:FLASHSALE.
      - `unresolved_details` integer: Ongoing product-scoped promotions whose product list could not be read in the served snapshot.
    - `last_attempt_at` string, nullable: When the sync last tried this shop (UTC).
    - `last_attempt_outcome` string, nullable
  - `not_covered` NotCovered[], required
    - `kind` string, required
    - `reason` string, required

## Other responses

- `422`: Validation Error

## Changes

> 76 revisions in range; 1 not diffed.

- **2026-10-26** `e153bb966d50`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/reacherapp/apis/reacher-data-api/changes/shop-promotions/:promotion_kind/:promotion_id/get.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/63ce74b30b5a?raw)
