Inbox
List Google Business Profile reviews, reply to them and read reviews across several locations in one call.
By the end of this page, you can list the reviews of a Google Business Profile (googlebusiness) location, reply to one and read reviews across several locations in one request.
You need a connected Google Business Profile account (accountId). Reviews are the only inbox surface on this platform.
| Feature | Supported |
|---|---|
| List reviews | |
| Reply to reviews | |
| Delete a reply | |
| Webhooks | (review.new, review.updated) |
| DMs | |
| Comments |
Step 1: list reviews
Call GET /v1/inbox/reviews?platform=googlebusiness (List reviews).
Filter with accountId, minRating, maxRating and hasReply. Page with cursor.
Each review's id is Google's full resource name, so it also encodes the location.
import Zernio from '@zernio/node';
const zernio = new Zernio();
const { data: reviews } = await zernio.reviews.listInboxReviews({
query: { platform: 'googlebusiness', accountId: '66b2e19d8c3f5a7e9d0b1c2d', hasReply: false }
});
console.log(reviews.data);Response (200):
{
"status": "success",
"data": [
{
"id": "accounts/123456789/locations/12345678901234567890/reviews/AIe9_BGx1234567890",
"platform": "googlebusiness",
"accountId": "66b2e19d8c3f5a7e9d0b1c2d",
"locationId": "12345678901234567890",
"locationName": "Joe's Pizza Downtown",
"reviewer": { "id": null, "name": "John Smith", "profileImage": "https://lh3.googleusercontent.com/a/..." },
"rating": 5,
"text": "Great service and friendly staff. Highly recommend.",
"created": "2026-08-15T10:30:00Z",
"hasReply": false,
"hasPhotos": false,
"photoCount": 0,
"photos": [],
"reply": null,
"reviewUrl": null
}
],
"pagination": { "hasMore": false, "nextCursor": null }
}Step 2: reply to a review
Call POST /v1/inbox/reviews/{reviewId}/reply with accountId and message (Reply to review).
reviewId is the review's id from Step 1, URL-encoded because it contains slashes.
Google keeps one owner reply per review. A second call overwrites the first, including a reply someone typed by hand in the Business Profile UI, so read the review before retrying.
Send an Idempotency-Key header to make a retry after a timeout safe. Review replies never count as outbound messages.
const { data: replied } = await zernio.reviews.replyToInboxReview({
path: { reviewId: 'accounts/123456789/locations/12345678901234567890/reviews/AIe9_BGx1234567890' },
body: {
accountId: '66b2e19d8c3f5a7e9d0b1c2d',
message: 'Thank you, John. See you again soon.'
}
});
console.log(replied.reply.created);Response (200):
{
"status": "success",
"reply": {
"id": "AIe9_BGx1234567890",
"text": "Thank you, John. See you again soon.",
"created": "2026-08-16T08:00:00Z"
},
"platform": "googlebusiness"
}DELETE /v1/inbox/reviews/{reviewId}/reply removes the reply and leaves the review in place.
New and edited reviews arrive on the review.new and review.updated webhooks.
Read reviews across several locations
Call POST /v1/accounts/{accountId}/gmb-reviews/batch with up to 50 locationNames (Batch get reviews).
Each name is accounts/{googleAccountId}/locations/{locationId}. Build it from the accountId and id values of List locations.
The response is a flat locationReviews array. Each item carries the location name it belongs to plus the review.
Google orders it newest first (orderBy defaults to updateTime desc), so stop paging once you cross your date window.
Paging. Send pageSize (50 at most). Send the response's nextPageToken back as pageToken in the next request body. A response with no nextPageToken is the last page.
Totals. Aggregate averageRating and totalReviewCount are not returned here. Get reviews has them for one location.
const { data: batch } = await zernio.accounts.batchGetGoogleBusinessReviews({
path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' },
body: {
locationNames: [
'accounts/123456789/locations/12345678901234567890',
'accounts/123456789/locations/22345678901234567890'
],
pageSize: 50
}
});
for (const item of batch.locationReviews) {
console.log(item.name, item.review.starRating, item.review.comment);
}Response (200):
{
"success": true,
"accountId": "66b2e19d8c3f5a7e9d0b1c2d",
"locationReviews": [
{
"name": "accounts/123456789/locations/22345678901234567890",
"review": {
"reviewId": "AIe9_BGx0987654321",
"name": "accounts/123456789/locations/22345678901234567890/reviews/AIe9_BGx0987654321",
"starRating": "FOUR",
"comment": "Good experience overall.",
"reviewer": { "displayName": "Anonymous", "isAnonymous": true },
"createTime": "2026-08-10T14:20:00Z",
"updateTime": "2026-08-10T14:20:00Z",
"reviewReply": null
}
}
],
"nextPageToken": "CiAKHAoUMTIzNDU2Nzg5"
}If it fails
A 403 on any reviews endpoint means the team has no inbox access, which is the first failure a legacy plan hits. Every account on usage-based billing includes it (pricing).
A 401 with code token_invalid means Google revoked or expired the account's token:
{
"error": "Access token invalid. Please reconnect your Google Business Profile account.",
"code": "token_invalid"
}Reconnect the account through GET /v1/connect/googlebusiness, then retry.
Empty data array for a location that shows reviews on Google: the location is not verified or not yet matched to its Maps place. Verification reports this as hasVoiceOfMerchant: false.
Error handling covers the envelope.
Related
- List reviews and Reply to review: the inbox API.
- Get reviews: one location with
averageRatingandtotalReviewCount. - Inbox webhooks: the
review.newandreview.updatedpayloads. - Multi-Location Posting: where the location names come from.