# REVIEWS.io

> Send REVIEWS.io company and product reviews to Dimension Labs with the universal API.

This brings in the reviews in your REVIEWS.io store, company reviews and product reviews: each review as a customer message, with its rating and product, and your public replies when there are any. One conversation per review. You run it on your side: a small script pulls from the REVIEWS.io API and posts each message to the [universal API](/api/tracker).

## What you need

- **The REVIEWS.io Plus plan.** REVIEWS.io says API access is only available on Plus.
- Your REVIEWS.io public **store key** and private **API key**. The private key adds private data to responses and is needed to list every review type.
- A Dimension Labs API key for a new **Universal API** source. See [Your API key](/api/tracker#your-api-key).

## 1. Get API access in REVIEWS.io

REVIEWS.io's steps are in [REVIEWS.io API](https://intercom.help/reviews-io/en/articles/9185068-reviews-io-api).

1. Log in to your REVIEWS.io account.
2. Go to **Integrations**, then select **API** from the integrations library. Copy your API key and keep it in a secrets manager. You also need your public store key, which REVIEWS.io's request examples call the store ID.
3. The API takes both as query parameters: `store` (the public store key) and `apikey` (the private key). Call it from your server only.

## 2. Pull new reviews

```bash
curl "https://api.reviews.io/reviews?store=YOUR_STORE&apikey=$REVIEWSIO_API_KEY&type=store_review&dateFrom=2026-10-01&sort=date_asc&per_page=100&page=1"
```

- "Retrieve Reviews and Questions" is the current endpoint. The older "Retrieve Company Reviews" and "Retrieve Product Reviews" endpoints are marked legacy and deprecated.
- **Types:** `type=store_review` for company reviews, `type=product_review` for product reviews. The code calls each in turn. With no `type`, the endpoint also returns third-party reviews and questions, and requires the API key.
- **Incremental:** `dateFrom` returns reviews newer than a date. Sort with `sort=date_asc`. There is no "updated since" filter on this endpoint.
- **Pagination:** `page` and `per_page`. `results_count` is the total with your filters applied.
- Public replies are in the same review, under `replies`, so there is no second call.
- **Rate limits:** REVIEWS.io doesn't publish one, and asks you to handle errors because its API can have short downtime. The code waits and retries on `429` and `5xx`.

## 3. Map the fields

Each review is one incoming message under its own `sessionId`. Each public reply is an outgoing message in the same session.

| Dimension Labs | REVIEWS.io | Notes |
|---|---|---|
| `text` | `title` and `comments` (incoming), `replies[].comments` (outgoing) | Join the title and body with a line break. Reviews with no text are skipped. |
| `userId` | | REVIEWS.io returns no reviewer id, so use the `sessionId`. Don't send `author.name`. |
| `incoming` | | `true` for the review, `false` for a reply. |
| `sessionId` | `id` | Already unique across types (for example `store_review-123456`). Prefixed `reviews-io-`. |
| `conversationId` | `id` | |
| `dimensionlabs_timestamp` | `date_created` (review and reply) | Convert with `toMillis`, which reads a date without a time zone as UTC. A reply within a second of the review is moved a second later. |
| `platformJson` | `type`, `rating`, `sku`, `order_id`, `source`, `tags`, `attributes` | `order_id` is your order reference and `sku` your product identifier: the fields to join to your own data. |

**Personal data.** With the API key, REVIEWS.io adds private data such as reviewer email addresses. Don't send it, or the reviewer's name and location: the code leaves the `author` object out. Reviews can still name people in their text: see [PII redaction](/data/pii-redaction).

## 4. Send it

```js
// reviews-io.mjs. Environment: REVIEWSIO_STORE, REVIEWSIO_API_KEY, DL_API_KEY.


const HOUR = 60 * 60 * 1000;
const DAY = 24 * HOUR;
const TYPES = ['store_review', 'product_review'];
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const at = (d, fallback) => { try { return d ? toMillis(d) : fallback; } catch { return fallback; } };

// Retries REVIEWS.io's rate limits (429) and server errors (5xx), honoring Retry-After when it is sent.
async function get(url) {
  for (let attempt = 0; ; attempt++) {
    const res = await fetch(url, { headers: { Accept: 'application/json' } });
    if (res.ok) return res.json();
    if ((res.status !== 429 && res.status < 500) || attempt === 5) throw new Error(`REVIEWS.io returned ${res.status}: ${await res.text()}`);
    await sleep((Number(res.headers.get('retry-after')) || 2 ** attempt) * 1000);
  }
}

async function send(r, historical) {
  const id = `reviews-io-${String(r.id).replace(/:/g, '-')}`;
  const created = toMillis(r.date_created);
  const base = { userId: id, sessionId: id, conversationId: id,
    platformJson: { type: r.type, rating: r.rating, sku: r.sku, orderId: r.order_id, source: r.source, tags: r.tags, attributes: r.attributes } };
  const text = [r.title, r.comments].filter((x) => x?.trim()).join('\n');
  if (text) {
    await sendToDimensionLabs({ ...base, text, incoming: true, dimensionlabs_timestamp: created },
      { historical: historical || created < Date.now() - HOUR });
  }
  let last = created;
  for (const reply of r.replies ?? []) {
    if (!reply.comments?.trim()) continue;
    last = Math.max(at(reply.date_created, last), last + 1000);
    await sendToDimensionLabs({ ...base, text: reply.comments.trim(), incoming: false, dimensionlabs_timestamp: last },
      { historical: historical || last < Date.now() - HOUR });
  }
}

// Sends every company and product review created from the day before `since` (Unix ms). Returns the new `since`.
export async function syncReviewsIo({ since, historical = false }) {
  const started = Date.now();
  const dateFrom = new Date(since - DAY).toISOString().slice(0, 10); // dateFrom takes a date: start a day early
  for (const type of TYPES) {
    for (let page = 1; ; page++) {
      const q = new URLSearchParams({ store: process.env.REVIEWSIO_STORE, apikey: process.env.REVIEWSIO_API_KEY,
        type, dateFrom, sort: 'date_asc', per_page: '100', page: String(page) });
      const { reviews = [] } = await get(`https://api.reviews.io/reviews?${q}`);
      for (const r of reviews) await send(r, historical);
      if (!reviews.length) break;
    }
  }
  return started;
}
```

Each run asks for every review from the day before the cursor, so a day of reviews is sent again on each run: those repeats are duplicates, removed automatically. This picks reviews up by creation date, so a reply written after the review was pulled is not sent. To catch those, run a history load over the last few weeks now and then (`node run.mjs` with a recent date): it sends the new replies, and everything else in it is a duplicate. Then run a backfill.

## 5. Run it

Save the code as `reviews-io.mjs` in a project set up as in [Set up your project](/api/tracker#set-up-your-project), and run it with the [scheduled sync](/api/scheduled-sync) runner: `import { syncReviewsIo as sync } from './reviews-io.mjs';`.

- **History first:** `node run.mjs 2020-01-01` sends every review since that date to `/trackhistorical`. Then start a [backfill](/dimensions/backfill) from the **Backfill** tab.
- **Then every 15 minutes:** the scheduled job sends new reviews to `/track`, and they are enriched automatically every day. Anything more than an hour old when the job sees it goes to `/trackhistorical`, and needs a backfill.
- **Re-runs are safe:** duplicates are removed automatically.

If you aren't on the Plus plan, or you have reviews in a file instead, [upload it](/data/upload-a-file) and check it first with the [file checker](/data/check-a-file), then run a backfill.

## REVIEWS.io reference

- [REVIEWS.io API (help center)](https://intercom.help/reviews-io/en/articles/9185068-reviews-io-api)
- [Retrieve Reviews and Questions](https://developer.reviews.io/reference/899f58c2c4431ec1000ec59a38b2916b)
- [Retrieve Company Reviews (legacy)](https://developer.reviews.io/reference/63143d3ccb82d8acc949f9e2068a4d30)
- [Retrieve Product Reviews (legacy)](https://developer.reviews.io/reference/09a10508634b01f463d1beacf3471b6a)

Checked against REVIEWS.io's documentation on 2026-10-26.
