Skip to main content
GET
/
api
/
ReturnInventory
/
list
List return inventories with pagination
curl --request GET \
  --url https://api.returnshelper.com/uat/user/api/ReturnInventory/list \
  --header 'x-rr-apikey: <api-key>' \
  --header 'x-rr-apitoken: <api-key>'
{
  "data": [
    {
      "returnInventoryId": 123,
      "returnRequestId": 123,
      "returnRequestLineItemId": "<string>",
      "rma": "<string>",
      "itemRma": "<string>",
      "sku": "<string>",
      "handlingCode": "<string>",
      "handlingStatusCode": "<string>",
      "warehouseId": 123,
      "createOn": "2023-11-07T05:31:56Z"
    }
  ],
  "totalNumberOfRecords": 123
}
This is not the recommended way to track return inventory state. The Return Helper API’s source of truth for inventory lifecycle is the webhook event streammarkShipmentArrive, newInventoryCreated, vasUpdated, the inventory-handling-complete event, and notifyUserRmaSwapped deliver every state change to your endpoint as it happens. Build your integration around webhooks; this list endpoint exists for one-time backfill and operational reconciliation only.
Returns a paginated list of return inventory records — the warehouse-side per-item state. Each record represents a single received item with its current handling decision, RMA, SKU mapping, and warehouse assignment.

When (and only when) to call

  • One-time backfill when first integrating, to populate a local database with existing inventory before subscribing to webhooks.
  • Periodic reconciliation to detect dropped or out-of-order webhook deliveries — diff your local cache against this endpoint’s results.
For ongoing state — knowing when a parcel is logged, when handling completes, when an RMA is reassigned — subscribe to webhooks. Polling this endpoint to discover state changes is unsupported and will produce stale data under load.

Required parameters

  • createFrom / createTo — both required, ISO 8601 timestamps. The window is capped at 62 days (SearchConfig.simpleRecordsMaxDays); wider ranges are rejected with a soft-error. See Window semantics below for the exact rule and what “62 days” really means.
  • pageSize — between 1 and 50 inclusive.
  • offset — non-negative integer. Combine with pageSize for offset-based pagination.

Response notes

  • totalNumberOfRecords (top-level field, sibling to data) is the row count for the current window — use it as the upper bound when paginating.
  • handlingCode reflects the current decision on the item — call Update return inventory handling to change it.
  • handlingStatusCode reflects the workflow state for that handling decision; translate via Get all handling statuses.
  • An item’s RMA (rma) is the warehouse-assigned identifier, not your seller-side reference numbers.

Backfilling historical inventory

Use this endpoint to seed your local database with every inventory record you’ve ever had, then switch to webhooks for everything afterwards. Because the API caps each request at 62 days, you walk the timeline in 62-day windows, paginate inside each window, and step backwards until you reach your account’s start date.

Window semantics

Two rules to internalize before you build the loop — they’re the difference between a clean backfill and a backfill that silently drops a day.
  1. Validator rule (calendar-day diff):
    createTo.Date − createFrom.Date  ≤  62
    
    The time-of-day is stripped before comparison. So a request with createFrom = 2024-03-13T15:00:00Z and createTo = 2024-05-14T09:00:00Z is valid (May 14 − Mar 13 = 62 days), even though wall-clock difference is less than 62 × 24h.
  2. Data filter (inclusive both ends, by full day):
    createOn ≥ createFrom.BeginOfDay()   AND   createOn ≤ createTo.EndOfDay()
    
    That is, the server expands createFrom to that day’s 00:00:00.000 and createTo to that day’s 23:59:59.999 before filtering. So a single legal request actually covers 63 consecutive calendar days of data (the full createFrom day, the full createTo day, and every day in between).
The practical consequence for a sliding-window backfill: if you reuse createFrom from window N as createTo for window N+1, the boundary day appears in both results. That’s a duplicate, not a gap — the algorithm never misses records, only over-fetches by ~1 day per window seam. As long as your local store uses returnInventoryId as the primary key with UPSERT (or INSERT IGNORE) semantics, the duplicates collapse and the final state is exact. If you’d rather avoid the duplicate fetch entirely, step windowEnd = windowStart − 1 day between iterations instead of windowEnd = windowStart. Each window then covers a fresh 63-day slice with no overlap. Either approach is correct; the safe-overlap variant below is the recommended default because it tolerates clock skew between client and server.

Backfill algorithm

  1. Pick a historyStart (e.g. the date your account was provisioned).
  2. Start with windowEnd = now().
  3. Compute windowStart = max(windowEnd − 62 days, historyStart).
  4. Inside the window, page from offset = 0 in pageSize chunks until offset ≥ totalNumberOfRecords. UPSERT each row keyed by returnInventoryId.
  5. Set windowEnd = windowStart and repeat from step 3 until windowEnd ≤ historyStart.
  6. From this point on, maintain your local store off the newInventoryCreated webhook (and the other inventory-lifecycle events). Re-running this backfill is unnecessary unless you suspect webhook data loss.

Sample code

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;
    }
  }
}
See Error codes for how to interpret and handle the API’s error responses.

Authorizations

x-rr-apikey
string
header
required

Your API key

x-rr-apitoken
string
header
required

Your API token — keep this private

Query Parameters

pageSize
integer
required

Number of records per page (1–50)

Required range: 1 <= x <= 50
offset
integer
required
createFrom
string<date-time>
required
createTo
string<date-time>
required

Response

Success

data
object[]

List of return inventories

totalNumberOfRecords
integer<int32>

Total count of return inventories