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

# 列出 Return Inventories（分页）

<Warning>
  本端点**不**是追踪退件库存状态的推荐方式。Return Helper API 中库存生命周期的真实数据源是 [Webhook 事件流](/zh-Hans/webhooks)——`markShipmentArrive`、`newInventoryCreated`、`vasUpdated`、库存处理完成事件、`notifyUserRmaSwapped` 等会在状态变更时即时推送至您的端点。请围绕 webhook 构建集成；本列表端点仅用于一次性回填与运营对账。
</Warning>

返回退件库存记录的分页列表——即仓库侧的逐项状态。每条记录代表一个已收到的物品，包含当前的处理决定、RMA、SKU 映射与所属仓库。

<h2 id="when-and-only-when-to-call">
  仅在以下情况调用
</h2>

* **一次性回填**：首次集成时，于订阅 webhook 之前，将既有库存写入本地数据库。
* **定期对账**：用以侦测遗漏或乱序的 webhook 推送——将本地缓存与本端点结果比对。

至于持续的状态——例如包裹被登记、处理完成、RMA 被重派——请订阅 webhook。轮询本端点以察觉状态变更不受支持，且在高负载下会产生过期数据。

<h2 id="required-parameters">
  必要参数
</h2>

* **`createFrom` / `createTo`** — 均必填，ISO 8601 时间戳。范围上限为 **62 天**（`SearchConfig.simpleRecordsMaxDays`），超出会返回软错误。具体语义请见下方的 [窗口语义](#window-semantics)。
* **`pageSize`** — 介于 `1` 与 `50` 之间。
* **`offset`** — 非负整数。配合 `pageSize` 用于按位移分页。

<h2 id="response-notes">
  响应说明
</h2>

* `totalNumberOfRecords`（顶层字段，与 `data` 同层）为当前窗口的总条数——分页时以此作为上限。
* `handlingCode` 反映当前的处理决定——可呼叫 [更新退件库存处理](/zh-Hans/api-reference/returninventory/update-return-inventory-handling) 修改。
* `handlingStatusCode` 反映该处理决定的工作流状态；可透过 [取得所有处理状态](/zh-Hans/api-reference/handlingstatus/get-all-handling-statuses) 转译。
* 物品的 RMA（`rma`）是仓库分配的识别码，并非您卖家侧的参考号。

<h2 id="backfilling-historical-inventory">
  历史库存回填
</h2>

可用本端点把每一条既有的退件库存载入您的本地数据库，之后一律改用 webhook 接收后续事件。由于 API 对每次请求的时间范围上限为 62 天，整体流程是「以 62 天为一格的滑动窗口，每格窗口内分页、窗口依序往前推」，直至覆盖账号开通日。

<h3 id="window-semantics">
  窗口语义
</h3>

开始写循环前，请先理解以下两条规则——这是「干净回填」与「静默漏一天」之间的分界线。

1. **验证器规则（日历日差）：**
   ```
   createTo.Date − createFrom.Date  ≤  62
   ```
   比较前会先把两端的时分秒归零。所以 `createFrom = 2024-03-13T15:00:00Z`、`createTo = 2024-05-14T09:00:00Z` 的请求仍然有效（`May 14 − Mar 13 = 62` 天），即使实际墙钟时长略短于 62 × 24 小时。

2. **数据过滤（两端整日均包含）：**
   ```
   createOn ≥ createFrom.BeginOfDay()   AND   createOn ≤ createTo.EndOfDay()
   ```
   也就是说，服务端会把 `createFrom` 扩展到当日 `00:00:00.000`，`createTo` 扩展到当日 `23:59:59.999`，再进行过滤。因此**一次合法请求实际覆盖 63 个连续日历日的数据**（createFrom 当日整天、createTo 当日整天、以及之间所有天）。

对滑动窗口回填的实际影响：若把窗口 N 的 `createFrom` 直接用作窗口 N+1 的 `createTo`，那一天会同时出现在两个窗口的结果中。这是**重复**，不是**漏单**——算法绝不会漏单，仅在每个窗口接缝处多抓约一天的数据。只要本地数据表以 `returnInventoryId` 为主键并使用 UPSERT（或 INSERT IGNORE），重复自动合并，最终数据完全正确。

如要彻底避免重复抓取，每次迭代后改为 `windowEnd = windowStart − 1 day`（而非 `windowEnd = windowStart`），每个窗口就是无重叠的全新 63 天切片。两种写法都正确；下方示例采用「容许接缝重叠」版本，因为它对客户端与服务端之间的时钟漂移更宽容，是推荐的默认。

<h3 id="backfill-algorithm">
  回填算法
</h3>

1. 选定 `historyStart`（例如账号开通日）。
2. 以 `windowEnd = now()` 起步。
3. 计算 `windowStart = max(windowEnd − 62 天, historyStart)`。
4. 在窗口内，从 `offset = 0` 开始按 `pageSize` 翻页，直到 `offset ≥ totalNumberOfRecords`。每条记录以 `returnInventoryId` 为主键 UPSERT 写入本地数据库。
5. 设 `windowEnd = windowStart`，回到第 3 步继续，直至 `windowEnd ≤ historyStart`。
6. 之后改由 `newInventoryCreated` webhook（以及其他库存生命周期事件）维护本地数据；除非怀疑 webhook 数据丢失，否则无需重跑本回填。

<h3 id="sample-code">
  代码示例
</h3>

<CodeGroup>
  ```java Java theme={null}
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.time.Instant;
  import java.time.temporal.ChronoUnit;

  // Requires a JSON library on the classpath. Below uses org.json for brevity:
  //   <dependency><groupId>org.json</groupId><artifactId>json</artifactId></dependency>
  import org.json.JSONArray;
  import org.json.JSONObject;

  class Main {
    static final String BASE_URL  = "https://api.returnhelpercentre.com/v1/user"; // production
    // sandbox: "https://api.returnshelper.com/uat/user"
    static final String API_KEY   = "<your api key>";
    static final String API_TOKEN = "<your api token>";

    static final int PAGE_SIZE   = 50;
    static final int WINDOW_DAYS = 62; // server cap: createTo.Date - createFrom.Date <= 62

    public static void main(String[] args) throws Exception {
      Instant historyStart = Instant.parse("2024-01-01T00:00:00Z"); // backfill anchor
      Instant windowEnd    = Instant.now();                         // walk backwards from now

      HttpClient http = HttpClient.newHttpClient();

      while (windowEnd.isAfter(historyStart)) {
        Instant windowStart = windowEnd.minus(WINDOW_DAYS, ChronoUnit.DAYS);
        if (windowStart.isBefore(historyStart)) {
          windowStart = historyStart;
        }

        int offset = 0;
        int total  = Integer.MAX_VALUE;

        while (offset < total) {
          String url = BASE_URL + "/api/ReturnInventory/list"
                     + "?pageSize="   + PAGE_SIZE
                     + "&offset="     + offset
                     + "&createFrom=" + windowStart
                     + "&createTo="   + windowEnd;

          HttpRequest req = HttpRequest.newBuilder()
              .uri(URI.create(url))
              .header("x-rr-apikey",   API_KEY)
              .header("x-rr-apitoken", API_TOKEN)
              .header("Accept",        "application/json")
              .GET()
              .build();

          HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString());
          JSONObject body = new JSONObject(resp.body());

          // Standard envelope: business status is in meta.status, not HTTP status.
          JSONObject meta = body.optJSONObject("meta");
          if (meta == null || meta.optInt("status") != 200) {
            System.err.println("list call failed: " + (meta == null ? "no meta" : meta.toString()));
            return;
          }

          total = body.optInt("totalNumberOfRecords", 0);
          JSONArray rows = body.optJSONArray("data");
          if (rows != null) {
            for (int i = 0; i < rows.length(); i++) {
              JSONObject inv = rows.getJSONObject(i);
              // TODO: UPSERT into your DB with returnInventoryId as primary key.
              //       Adjacent windows share a seam day, so the same row may be
              //       returned twice; UPSERT absorbs the duplicate.
              //   inv.getLong("returnInventoryId")
              //   inv.getString("handlingCode")
              //   inv.getString("handlingStatusCode")
              //   inv.getInt("warehouseId")
            }
          }

          offset += PAGE_SIZE;
          Thread.sleep(200); // light throttle
        }

        windowEnd = windowStart;
      }
    }
  }
  ```

  ```js Node.js theme={null}
  // Requires Node.js 18+ for built-in fetch.

  const BASE_URL  = 'https://api.returnhelpercentre.com/v1/user'; // production
  // sandbox: 'https://api.returnshelper.com/uat/user'
  const API_KEY   = '<your api key>';
  const API_TOKEN = '<your api token>';

  const PAGE_SIZE   = 50;
  const WINDOW_DAYS = 62; // server cap: createTo.Date - createFrom.Date <= 62

  const MS_PER_DAY = 24 * 60 * 60 * 1000;

  async function main() {
    const historyStart = new Date('2024-01-01T00:00:00Z'); // backfill anchor
    let windowEnd      = new Date();                       // walk backwards from now

    while (windowEnd > historyStart) {
      let windowStart = new Date(windowEnd.getTime() - WINDOW_DAYS * MS_PER_DAY);
      if (windowStart < historyStart) {
        windowStart = historyStart;
      }

      let offset = 0;
      let total  = Infinity;

      while (offset < total) {
        const params = new URLSearchParams({
          pageSize:   String(PAGE_SIZE),
          offset:     String(offset),
          createFrom: windowStart.toISOString(),
          createTo:   windowEnd.toISOString(),
        });

        const resp = await fetch(`${BASE_URL}/api/ReturnInventory/list?${params}`, {
          headers: {
            'x-rr-apikey':   API_KEY,
            'x-rr-apitoken': API_TOKEN,
            'Accept':        'application/json',
          },
        });
        const body = await resp.json();

        // Standard envelope: business status is in meta.status, not HTTP status.
        if (body?.meta?.status !== 200) {
          console.error('list call failed:', body?.meta);
          return;
        }

        total      = body.totalNumberOfRecords ?? 0;
        const rows = body.data ?? [];

        for (const inv of rows) {
          // TODO: UPSERT into your DB with returnInventoryId as primary key.
          //       Adjacent windows share a seam day, so the same row may be
          //       returned twice; UPSERT absorbs the duplicate.
          //   inv.returnInventoryId
          //   inv.handlingCode
          //   inv.handlingStatusCode
          //   inv.warehouseId
        }

        offset += PAGE_SIZE;
        await new Promise((r) => setTimeout(r, 200)); // light throttle
      }

      windowEnd = windowStart;
    }
  }

  main().catch(console.error);
  ```

  ```ts TypeScript theme={null}
  // Requires Node.js 18+ for built-in fetch (or a fetch polyfill in older runtimes).

  const BASE_URL  = 'https://api.returnhelpercentre.com/v1/user'; // production
  // sandbox: 'https://api.returnshelper.com/uat/user'
  const API_KEY   = '<your api key>';
  const API_TOKEN = '<your api token>';

  const PAGE_SIZE   = 50;
  const WINDOW_DAYS = 62; // server cap: createTo.Date - createFrom.Date <= 62

  const MS_PER_DAY = 24 * 60 * 60 * 1000;

  interface ApiEnvelope<T> {
    correlationId: string;
    meta: {
      status: number;
      errorCode?: string | null;
      error?: Record<string, unknown>;
    };
    data?: T[];
    totalNumberOfRecords?: number;
  }

  interface ReturnInventoryRow {
    returnInventoryId: number;
    handlingCode: string;
    handlingStatusCode: string;
    warehouseId: number;
    // ...other fields documented in the API reference
  }

  async function main(): Promise<void> {
    const historyStart = new Date('2024-01-01T00:00:00Z'); // backfill anchor
    let windowEnd      = new Date();                       // walk backwards from now

    while (windowEnd > historyStart) {
      let windowStart = new Date(windowEnd.getTime() - WINDOW_DAYS * MS_PER_DAY);
      if (windowStart < historyStart) {
        windowStart = historyStart;
      }

      let offset = 0;
      let total  = Infinity;

      while (offset < total) {
        const params = new URLSearchParams({
          pageSize:   String(PAGE_SIZE),
          offset:     String(offset),
          createFrom: windowStart.toISOString(),
          createTo:   windowEnd.toISOString(),
        });

        const resp = await fetch(`${BASE_URL}/api/ReturnInventory/list?${params}`, {
          headers: {
            'x-rr-apikey':   API_KEY,
            'x-rr-apitoken': API_TOKEN,
            'Accept':        'application/json',
          },
        });
        const body = (await resp.json()) as ApiEnvelope<ReturnInventoryRow>;

        // Standard envelope: business status is in meta.status, not HTTP status.
        if (body.meta?.status !== 200) {
          console.error('list call failed:', body.meta);
          return;
        }

        total      = body.totalNumberOfRecords ?? 0;
        const rows = body.data ?? [];

        for (const inv of rows) {
          // TODO: UPSERT into your DB with returnInventoryId as primary key.
          //       Adjacent windows share a seam day, so the same row may be
          //       returned twice; UPSERT absorbs the duplicate.
          //   inv.returnInventoryId
          //   inv.handlingCode
          //   inv.handlingStatusCode
          //   inv.warehouseId
        }

        offset += PAGE_SIZE;
        await new Promise((r) => setTimeout(r, 200)); // light throttle
      }

      windowEnd = windowStart;
    }
  }

  main().catch(console.error);
  ```

  ```apex Apex theme={null}
  // Salesforce Apex — callable from an execute-anonymous window or wrapped in a
  // Queueable for large backfills (single-transaction callout limit is 100).
  //
  // Add the API host to "Remote Site Settings" before running.

  public class Main {
      private static final String BASE_URL  = 'https://api.returnhelpercentre.com/v1/user'; // production
      // sandbox: 'https://api.returnshelper.com/uat/user'
      private static final String API_KEY   = '<your api key>';
      private static final String API_TOKEN = '<your api token>';

      private static final Integer PAGE_SIZE   = 50;
      private static final Integer WINDOW_DAYS = 62; // server cap: createTo.Date - createFrom.Date <= 62

      public static void main() {
          Datetime historyStart = Datetime.newInstanceGmt(2024, 1, 1, 0, 0, 0); // backfill anchor
          Datetime windowEnd    = Datetime.now();                               // walk backwards from now

          Http http = new Http();

          while (windowEnd > historyStart) {
              Datetime windowStart = windowEnd.addDays(-WINDOW_DAYS);
              if (windowStart < historyStart) {
                  windowStart = historyStart;
              }

              Integer offset = 0;
              Integer total  = Integer.MAX_VALUE;

              while (offset < total) {
                  String url = BASE_URL + '/api/ReturnInventory/list'
                      + '?pageSize='   + PAGE_SIZE
                      + '&offset='     + offset
                      + '&createFrom=' + EncodingUtil.urlEncode(formatGmt(windowStart), 'UTF-8')
                      + '&createTo='   + EncodingUtil.urlEncode(formatGmt(windowEnd),   'UTF-8');

                  HttpRequest req = new HttpRequest();
                  req.setMethod('GET');
                  req.setEndpoint(url);
                  req.setHeader('x-rr-apikey',   API_KEY);
                  req.setHeader('x-rr-apitoken', API_TOKEN);
                  req.setHeader('Accept',        'application/json');

                  HttpResponse resp = http.send(req);

                  Map<String, Object> body = (Map<String, Object>) JSON.deserializeUntyped(resp.getBody());
                  Map<String, Object> meta = (Map<String, Object>) body.get('meta');

                  // Standard envelope: business status is in meta.status, not HTTP status.
                  if (meta == null || (Integer) meta.get('status') != 200) {
                      System.debug('list call failed: ' + meta);
                      return;
                  }

                  Object totalObj = body.get('totalNumberOfRecords');
                  total = (totalObj == null) ? 0 : (Integer) totalObj;

                  List<Object> rows = (List<Object>) body.get('data');
                  if (rows != null) {
                      for (Object row : rows) {
                          Map<String, Object> inv = (Map<String, Object>) row;
                          // TODO: UPSERT into your custom object with returnInventoryId__c
                          //       as External Id. Adjacent windows share a seam day, so the
                          //       same row may be returned twice; UPSERT absorbs the duplicate.
                          //   inv.get('returnInventoryId')
                          //   inv.get('handlingCode')
                          //   inv.get('handlingStatusCode')
                          //   inv.get('warehouseId')
                      }
                  }

                  offset += PAGE_SIZE;
              }

              windowEnd = windowStart;
          }
      }

      private static String formatGmt(Datetime dt) {
          return dt.formatGmt('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'');
      }
  }
  ```
</CodeGroup>

<h2 id="related">
  相关
</h2>

* [取得退件库存详情](/zh-Hans/api-reference/returninventory/get-return-inventory-details) — 当 webhook 提及未缓存的 `returnInventoryId` 时，按 ID 取得单条记录。
* [按行项目 ID 取得退件库存](/zh-Hans/api-reference/returninventory/get-return-inventory-by-line-item-id) — 按您的 `returnRequestLineItemId` 查询。
* [更新退件库存处理](/zh-Hans/api-reference/returninventory/update-return-inventory-handling) — 写入侧端点。
* [取得所有处理状态](/zh-Hans/api-reference/handlingstatus/get-all-handling-statuses) 与 [取得所有处理类型](/zh-Hans/api-reference/handling/get-all-handling-types) — 代码到标签的映射。
* [Webhooks](/zh-Hans/webhooks) — 库存生命周期事件的标准管道。请先设置 webhook 再使用本端点。

如需了解错误响应的解读与处理，请参阅 [Error codes](/zh-Hans/reference/error-codes)。


## OpenAPI

````yaml get /api/ReturnInventory/list
openapi: 3.1.0
info:
  title: Return Helper API
  description: API documentation for Return Helper — covering User and Public endpoints.
  version: 1.0.0
servers:
  - url: https://api.returnshelper.com/uat/user
    description: Sandbox — User API
  - url: https://api.returnshelper.com/uat/public
    description: Sandbox — Public API
  - url: https://api.returnhelpercentre.com/v1/user
    description: Production — User API
  - url: https://api.returnhelpercentre.com/v1/public
    description: Production — Public API
  - url: https://api.returnhelperchina.com/user
    description: Production — User API (China)
security:
  - ApiKey: []
    ApiToken: []
paths:
  /api/ReturnInventory/list:
    get:
      tags:
        - ReturnInventory
      summary: List return inventories with pagination
      operationId: ReturnUserApi_ReturnInventory_List
      parameters:
        - name: pageSize
          in: query
          required: true
          schema:
            type: integer
            maximum: 50
            minimum: 1
          description: Number of records per page (1–50)
        - name: offset
          in: query
          required: true
          schema:
            type: integer
        - name: createFrom
          in: query
          required: true
          schema:
            type: string
            format: date-time
        - name: createTo
          in: query
          required: true
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: >-
                  #/components/schemas/PaginationResponse_List_UserListReturnInventoryReply
        '401':
          description: >-
            Authentication failed. Returned when the `x-rr-apikey` or
            `x-rr-apitoken` header is missing or invalid. The body uses the
            standard `ApiResponse` envelope with `meta.error.message` describing
            the auth failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiResponse'
      security:
        - ApiKey: []
          ApiToken: []
      servers:
        - url: https://api.returnshelper.com/uat/user
          description: Sandbox — User API
components:
  schemas:
    PaginationResponse_List_UserListReturnInventoryReply:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/UserListReturnInventoryReply'
          description: List of return inventories
        totalNumberOfRecords:
          type: integer
          format: int32
          description: Total count of return inventories
    ApiResponse:
      type: object
      description: >-
        Universal response envelope. Successful responses include the business
        payload as additional top-level fields alongside `correlationId` and
        `meta`. Failed responses (auth errors, validation errors) only populate
        `correlationId` and `meta`, with `meta.errorCode` and `meta.error`
        describing the failure.
      properties:
        correlationId:
          type:
            - string
            - 'null'
          description: >-
            Unique correlation ID for tracing the request through Return Helper
            systems.
        meta:
          $ref: '#/components/schemas/ApiResponseMeta'
    UserListReturnInventoryReply:
      type: object
      properties:
        returnInventoryId:
          type: integer
          format: int32
        returnRequestId:
          type: integer
          format: int32
        returnRequestLineItemId:
          type:
            - string
            - 'null'
        rma:
          type:
            - string
            - 'null'
        itemRma:
          type:
            - string
            - 'null'
        sku:
          type:
            - string
            - 'null'
        handlingCode:
          type:
            - string
            - 'null'
        handlingStatusCode:
          type:
            - string
            - 'null'
        warehouseId:
          type: integer
          format: int32
        createOn:
          type: string
          format: date-time
    ApiResponseMeta:
      type: object
      description: >-
        Application-level metadata for every API response. Inspect `status` and
        `errorCode` to detect soft-error responses (validation failures arrive
        as HTTP 200 with `meta.status: 400`).
      properties:
        status:
          type: integer
          description: >-
            Application-level status code. For successful operations this
            mirrors the HTTP status (e.g. 200). For validation failures it
            reports the logical status (e.g. 400) even though the wire HTTP
            status is 200.
        data:
          type: object
          additionalProperties:
            type: string
          description: Reserved free-form metadata key/value pairs. Usually empty.
        errorCode:
          type:
            - string
            - 'null'
          description: >-
            Machine-readable error code (e.g. `VALIDATION_FAILED`). Non-null
            only when the operation failed.
        error:
          type: object
          additionalProperties: true
          description: >-
            Field-level or message-level error detail keyed by request property
            name. Empty object on success.
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: x-rr-apikey
      description: Your API key
    ApiToken:
      type: apiKey
      in: header
      name: x-rr-apitoken
      description: Your API token — keep this private

````