<!-- Reviews API Errors: Codes, Causes and Fixes · Prisma · https://prismareviews.com/docs/errors -->
> Reviews API errors explained: every Prisma error code with its HTTP status, what it means and what to do, plus how Google, App Store and Trustpilot errors reach you.

# Reviews API errors and how to fix them

Prisma returns one error format for every endpoint and every platform. Each error has an HTTP status, a stable `code`, a `retryable` flag and a `request_id`. Most errors you'll see in production are one of three things: a connection that needs reauthorization, the per-key rate limit, or a reply the platform won't take.

## Error format

```json
{
  "error": {
    "type": "conflict_error",
    "code": "connection_reauth_required",
    "message": "The connection for this location is not active. Reconnect it and try again.",
    "retryable": false,
    "request_id": "req_01KJ8T3C5D7E9F1G3H5J7K9M1N",
    "docs_url": "https://prismareviews.com/docs/errors#connection_reauth_required",
    "connection_id": "con_01KJ8P7R5S3T1V9W7X5Y3Z1A2B"
  }
}
```

| Field | Description |
| --- | --- |
| `type` | The family of the error, from the HTTP status (table below). |
| `code` | Stable, machine-readable code. Branch on this, not on `message`. |
| `message` | Human-readable explanation. It can change; don't parse it. |
| `retryable` | `true` for `429` and `5xx` errors: the same request can succeed later. |
| `request_id` | The `req_…` ID, also in the `Request-Id` header. Log it and include it when you contact support. |
| `docs_url` | Link to the description of this code on this page: `https://prismareviews.com/docs/errors#<code>`. |

Some errors add fields: `max_length` and `platform` on `reply_too_long`, `platform` and `url` on `reply_not_supported`, `platform` on `plan_upgrade_required`, `connection_id` on `connection_reauth_required`.

| `type` | HTTP status |
| --- | --- |
| `authentication_error` | 401 |
| `permission_error` | 403 |
| `not_found_error` | 404 |
| `conflict_error` | 409 |
| `invalid_request_error` | 422 |
| `rate_limit_error` | 429 |
| `api_error` | 500, 501 |

## All error codes

| HTTP | `code` | Retryable | Meaning |
| --- | --- | --- | --- |
| 401 | [`missing_api_key`](#missing_api_key) | No | No API key, or not shaped like one. |
| 401 | [`invalid_api_key`](#invalid_api_key) | No | The key doesn't exist or was revoked. |
| 403 | [`plan_upgrade_required`](#plan_upgrade_required) | No | The platform isn't in your plan. |
| 404 | [`resource_not_found`](#resource_not_found) | No | No object with that ID in your workspace and mode. |
| 404 | [`reply_not_found`](#reply_not_found) | No | The review has no reply to delete. |
| 404 | [`route_not_found`](#route_not_found) | No | The method and path don't exist. |
| 409 | [`connection_reauth_required`](#connection_reauth_required) | No | The location's connection is not active. |
| 409 | [`idempotency_conflict`](#idempotency_conflict) | No | The `Idempotency-Key` was used with a different request. |
| 422 | [`invalid_request`](#invalid_request) | No | A parameter or the body is missing or invalid. |
| 422 | [`invalid_credentials`](#invalid_credentials) | No | Platform credentials are malformed or were rejected. |
| 422 | [`operation_not_supported`](#operation_not_supported) | No | The platform's API can't do this. |
| 422 | [`reply_not_supported`](#reply_not_supported) | No | The platform is read only: Prisma can't reply there. |
| 422 | [`reply_too_long`](#reply_too_long) | No | The reply is over the platform's limit. |
| 429 | [`rate_limited`](#rate_limited) | Yes | Too many requests this minute for this key. |
| 500 | [`internal_error`](#internal_error) | Yes | Something failed on Prisma's side. |
| 501 | [`platform_not_available`](#platform_not_available) | Yes | The platform can't be connected with live keys yet. |

## Error reference

<h3 id="missing_api_key"><code>missing_api_key</code> · 401</h3>

**Type:** `authentication_error`. The request has no `Authorization` header, uses a scheme other than `Bearer`, or the value isn't shaped like a Prisma key (`pk_live_…` or `pk_test_…`).

**What to do:** send `Authorization: Bearer $PRISMA_API_KEY`. Check that the environment variable is set and that no quotes or spaces ended up in it. See [Authentication](/docs/api-reference/authentication).

<h3 id="invalid_api_key"><code>invalid_api_key</code> · 401</h3>

**Type:** `authentication_error`. The key is well formed but doesn't exist or was revoked.

**What to do:** use a current key. Keys can't be recovered, only replaced: email hello@prismareviews.com for a new one.

<h3 id="plan_upgrade_required"><code>plan_upgrade_required</code> · 403</h3>

**Type:** `permission_error`. You tried to connect a platform that isn't included in your plan. Yelp, Tripadvisor, Booking.com, Yotpo, REVIEWS.io and G2 start at Beam. The error includes `platform`.

**What to do:** upgrade (see [Pricing](/pricing)) or connect a platform included in Spark: Google Business Profile, App Store, Google Play, Judge.me or Trustpilot.

<h3 id="resource_not_found"><code>resource_not_found</code> · 404</h3>

**Type:** `not_found_error`. No review, location, connection or webhook endpoint with that ID exists in your workspace, in the mode of your key. A test key can't see live objects and a live key can't see test ones.

**What to do:** check the ID and that you use a key of the right mode. Get current IDs from the list endpoints.

<h3 id="reply_not_found"><code>reply_not_found</code> · 404</h3>

**Type:** `not_found_error`. `DELETE /v1/reviews/{id}/reply` was called on a review that has no reply.

**What to do:** nothing to delete. If you track replies locally, refresh the review with `GET /v1/reviews/{id}`.

<h3 id="route_not_found"><code>route_not_found</code> · 404</h3>

**Type:** `not_found_error`. The method and path combination doesn't exist, for example `GET /v1/review` or `PUT /v1/reviews/{id}/reply`.

**What to do:** check the path and method against the [API reference](/docs/api-reference). Every path starts with `/v1`.

<h3 id="connection_reauth_required"><code>connection_reauth_required</code> · 409</h3>

**Type:** `conflict_error`. You tried to reply to a review whose location belongs to a connection that isn't `active`: it is `reauth_required` (the platform rejected the stored token or key), `pending` or `disconnected`. The error includes `connection_id`.

**What to do:** don't retry as is. Delete the connection and create a new one, then reply again on the new location's review. You also get a `connection.updated` webhook when a connection becomes `reauth_required`. See [Connections](/docs/api-reference/connections).

<h3 id="idempotency_conflict"><code>idempotency_conflict</code> · 409</h3>

**Type:** `conflict_error`. The `Idempotency-Key` you sent was already used in your workspace for a different request (another body or another review).

**What to do:** use a new key for every distinct reply. Reuse a key only to retry exactly the same request.

<h3 id="invalid_request"><code>invalid_request</code> · 422</h3>

**Type:** `invalid_request_error`. A query parameter or body field is missing or invalid, or the body isn't valid JSON. The `message` names the field, for example `limit: Number must be less than or equal to 100`.

**What to do:** fix the request. Retrying it unchanged gives the same error.

<h3 id="invalid_credentials"><code>invalid_credentials</code> · 422</h3>

**Type:** `invalid_request_error`. On `POST /v1/connections`, the `credentials` don't have the fields the platform needs, or the platform rejected them when Prisma tried them. Nothing was stored.

**What to do:** read the `message`, which names the field or quotes the platform's answer, and check the fields on [Platforms](/docs/api-reference/platforms). For Google Play, check that Prisma's service account is invited for every package.

<h3 id="operation_not_supported"><code>operation_not_supported</code> · 422</h3>

**Type:** `invalid_request_error`. The platform's API can't do what you asked. Today this is deleting a reply on Google Play or Judge.me.

**What to do:** on Google Play, replace the reply with `POST` instead. On Judge.me, change it in Judge.me itself.

<h3 id="reply_not_supported"><code>reply_not_supported</code> · 422</h3>

**Type:** `invalid_request_error`. You sent `POST` or `DELETE /v1/reviews/{id}/reply` for a review on a read-only platform: Yelp, Tripadvisor, Booking.com, Yotpo, REVIEWS.io or G2. These platforms have no reply API for third parties. The error includes `platform` and `url`, the link to answer the review on the platform (for some platforms, the listing or product page).

**What to do:** answer the review on the platform with the link in `url`. Don't retry: nothing was stored or sent. To skip these reviews in your reply flow, check `platform` before you reply.

<h3 id="reply_too_long"><code>reply_too_long</code> · 422</h3>

**Type:** `invalid_request_error`. The reply is longer than the platform accepts: Google Business Profile 4,096 bytes, App Store 5,970 characters, Google Play 350 characters. The error includes `platform` and `max_length`.

**What to do:** shorten the text and send it again. Nothing was queued.

<h3 id="rate_limited"><code>rate_limited</code> · 429</h3>

**Type:** `rate_limit_error`. Your key made more requests this minute than your plan allows (Spark 60, Beam 120, Spectrum 600, Aurora 1,200 per minute). The `Retry-After` header says how many seconds until the window resets.

**What to do:** wait `Retry-After` seconds and retry. Spread bulk jobs over time and use webhooks instead of polling.

<h3 id="internal_error"><code>internal_error</code> · 500</h3>

**Type:** `api_error`. Something failed on Prisma's side. It is logged with the `request_id`.

**What to do:** retry with exponential backoff. If it keeps happening, email hello@prismareviews.com with the `request_id`.

<h3 id="platform_not_available"><code>platform_not_available</code> · 501</h3>

**Type:** `api_error`. The platform can't be connected with live keys yet. Every platform on [Platforms](/docs/api-reference/platforms) is available today, so you should not see it; it exists for platforms added in the future, which can be tried with a test key first.

**What to do:** use a test key to build against the platform until it is available with live keys.

## Retrying safely

Retry only when `retryable` is `true`, and back off exponentially. With `429`, wait `Retry-After` first.

```javascript
async function prisma(path, init = {}) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch("https://api.prismareviews.com/v1" + path, {
      ...init,
      headers: { Authorization: "Bearer " + process.env.PRISMA_API_KEY, ...init.headers },
    });
    if (res.ok) return res.json();
    const { error } = await res.json();
    if (!error.retryable) throw new Error(`${error.code}: ${error.message} (${error.request_id})`);
    const wait = Number(res.headers.get("Retry-After")) || 2 ** attempt;
    await new Promise((r) => setTimeout(r, wait * 1000));
  }
  throw new Error("Prisma API: too many retries");
}
```
```python
import os
import time
import requests

def prisma(method, path, **kw):
    for attempt in range(5):
        r = requests.request(
            method,
            "https://api.prismareviews.com/v1" + path,
            headers={"Authorization": f"Bearer {os.environ['PRISMA_API_KEY']}"},
            timeout=30,
            **kw,
        )
        if r.ok:
            return r.json()
        error = r.json()["error"]
        if not error["retryable"]:
            raise RuntimeError(f"{error['code']}: {error['message']} ({error['request_id']})")
        time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
    raise RuntimeError("Prisma API: too many retries")
```

When you retry `POST /v1/reviews/{id}/reply`, send the same `Idempotency-Key` so a reply is never posted twice.

## Errors from the platforms

Prisma never calls Google, Apple, Trustpilot or any other platform inside your request, so their errors don't come back as HTTP errors from Prisma. Syncs and replies run in a background queue, and platform errors reach you like this:

| The platform answers | What Prisma does | What you see |
| --- | --- | --- |
| 401 or 403 on the stored token or key (revoked access, deleted key, expired refresh token) | Marks the connection `reauth_required` and stops using it | `connection.updated` webhook with `status` and `reason`; replies return `409 connection_reauth_required` |
| 429 or 5xx (rate limited or down) | Retries with exponential backoff, 30 seconds doubling up to one hour, up to 8 attempts for a reply | Nothing, unless every attempt fails |
| Any other refusal of a reply (for example, a review the platform won't let you answer) | Gives up at once | `reply.status: "failed"`, `reply.error` with the platform's answer, and a `reply.failed` webhook |
| Rejected credentials when connecting | Stores nothing | `422 invalid_credentials` on `POST /v1/connections` |

A few platform specifics:

- **Google Business Profile:** Google refresh tokens stop working when the owner revokes access, after six months without use, or when too many tokens exist for the same account and client (see [Google OAuth 2.0](https://developers.google.com/identity/protocols/oauth2)). On Google, a 403 on a single location (for example, the account lost access to it) fails that reply instead of marking the connection. Google also limits edits per profile (see [Google Business Profile API usage limits](https://developers.google.com/my-business/content/limits)); Prisma retries those 429s for you.
- **App Store:** a revoked key, or a key without the Customer Support role, makes Apple answer 401 or 403, and the connection becomes `reauth_required`. Status codes are in Apple's [App Store Connect API HTTP status codes](https://developer.apple.com/documentation/appstoreconnectapi/about-the-http-status-code).
- **Trustpilot:** a 401 or 403 usually means the business user lost access to the profile or the account doesn't have Trustpilot's API module. See [Trustpilot common error messages](https://developers.trustpilot.com/common-error-messages) and the [Trustpilot API](/trustpilot-api) page.
- **Google Play:** a 401 or 403 means Prisma's service account is no longer invited for that app in Play Console. See the [Google Play reviews API](/google-play-reviews-api) page.

## FAQ

### Why does my Google connection stop working after a while?

Google refresh tokens stop working if the user revokes access, if the token goes unused for six months, or if too many tokens exist for the same account and client. Prisma marks the connection `reauth_required` and sends a `connection.updated` webhook; delete it and connect again.

### What should I do with a 429 error?

Wait the number of seconds in the `Retry-After` header and retry. The limit is per API key and per minute, set by your plan. Platform rate limits never reach you as 429: Prisma queues and retries those itself.

### Why can't I find an old Google Play review?

The Google Play API only returns reviews created or edited in the last 7 days. Prisma keeps every review it has seen since you connected, but it cannot import older ones.

### Do I get charged for failed requests?

No. Billing is per connected location, not per request, and requests over the rate limit are not even counted in your usage.

See also the [API reference](/docs/api-reference), the [Quickstart](/docs/quickstart) and the [Changelog](/changelog).
