Developer reference
SyncMyReviews Zapier API
This is the complete REST surface the SyncMyReviews app on Zapier calls. It covers authentication, the seven REST-hook triggers, two searches, four write actions, and the dropdowns that populate the Zap editor. Everything here is read-only reference material — no account is required to view it.
Overview
All Zapier endpoints live under the /api/zapier prefix on the production API host. There is no sandbox tier: the integration talks to production only.
| Base URL | https://api.syncmyreviews.com |
|---|---|
| Transport | HTTPS only. Plain HTTP is not served. |
| Content type | application/json for request and response bodies. |
| Timestamps | ISO 8601, UTC. |
| Tenancy | Every query is scoped to the account that owns the token. An id belonging to another account is indistinguishable from one that does not exist — both return 404. |
Google and Facebook are connected on the SyncMyReviews side. The Zapier app never talks to those platforms directly; it only calls the endpoints on this page.
Authentication
The integration uses a SyncMyReviews-issued API token sent as a bearer credential. Account owners mint the token from Connections → Zapier in the dashboard; the plaintext value is shown exactly once and is stored only as a hash. Members cannot generate tokens.
Authorization: Bearer <syncmyreviews-token>
Accept: application/jsonA token carries two abilities. Read endpoints (including subscribe and unsubscribe) require zapier:read; the four write actions additionally require zapier:write.
GET /api/zapier/auth/test
Called immediately after the user pastes a token. A 200 confirms the credential, and connection_label is what Zapier shows as the connection name.
{
"account_id": 1234,
"account_name": "Acme Coffee Group",
"connection_label": "Acme Coffee Group",
"token_last_four": "k9Xz",
"abilities": ["zapier:read", "zapier:write"]
}Revoking or rotating the token from the dashboard invalidates it immediately; subsequent calls return 401 and Zapier prompts the user to reconnect.
Error format
Every error uses one envelope: a stable machine error code, a message safe to show to the Zap builder, and an optional context object. The context key is omitted when empty.
{
"error": "subscription_inactive",
"message": "This business does not have an active subscription.",
"context": { "business_id": 521 }
}| Code | HTTP | When it happens |
|---|---|---|
missing_token | 401 | No bearer token was sent. |
invalid_token | 401 | The token does not resolve to an account. |
token_revoked | 401 | The token was revoked or has expired. |
ability_missing | 403 | The token lacks the ability the route requires. |
validation_failed | 422 | The payload failed validation. Field errors are nested under context.errors. |
subscription_inactive | 402 | The target business has no active or trialing subscription. |
business_not_found | 404 | The business id is unknown or belongs to another account. |
review_not_found | 404 | The review id is unknown or belongs to another account. |
subscription_not_found | 404 | The hook subscription id is unknown or belongs to another account. |
missing_google_connection | 409 | The business has no connected Google location. |
missing_facebook_connection | 409 | The business has no connected Facebook page. |
rate_limit_exceeded | 429 | The per-token action rate limit was exhausted. |
payload_too_large | 413 | The request body exceeded the size cap. |
internal_error | 500 | An unexpected server error. No stack trace is ever returned. |
Validation failures nest Laravel's field-level messages under context.errors, keyed by field name, so Zapier can surface them verbatim.
Endpoint index
| Method | URI | Ability | Purpose |
|---|---|---|---|
| GET | /api/zapier/auth/test | zapier:read | Verify a token and return the connection label. |
| GET | /api/zapier/businesses | zapier:read | Business dropdown, including subscription state. |
| GET | /api/zapier/tags | zapier:read | Tag dropdown for the account. |
| GET | /api/zapier/sources | zapier:read | Review-source dropdown (server-side constant list). |
| POST | /api/zapier/triggers/{triggerKey}/subscriptions | zapier:read | Subscribe a Zap to a trigger (REST hook). |
| DELETE | /api/zapier/triggers/{triggerKey}/subscriptions/{subscriptionId} | zapier:read | Unsubscribe a Zap from a trigger. |
| GET | /api/zapier/triggers/{triggerKey}/sample | zapier:read | Single sample event for Zapier's test step. |
| GET | /api/zapier/searches/businesses | zapier:read | Find a business by name or exact slug. |
| GET | /api/zapier/searches/reviews | zapier:read | Find a review by source and external id. |
| POST | /api/zapier/actions/manual-reviews | zapier:write | Create a manual review. |
| PATCH | /api/zapier/actions/reviews/{reviewId}/visibility | zapier:write | Hide or show a review. |
| POST | /api/zapier/actions/google-posts | zapier:write | Schedule a Google Business Profile post. |
| POST | /api/zapier/actions/facebook-posts | zapier:write | Schedule a Facebook Page post. |
Dropdowns
These three endpoints populate the pickers in the Zap editor. All of them return a data envelope.
GET /api/zapier/businesses
Every business under the account, including those whose subscription has lapsed, so a user can see that a Zap is wired up even when it will not fire yet. display_label is the string Zapier renders.
{
"data": [
{
"id": 521,
"name": "Acme Coffee — Downtown",
"has_active_subscription": true,
"display_label": "Acme Coffee — Downtown"
},
{
"id": 522,
"name": "Acme Coffee — Suburb",
"has_active_subscription": false,
"display_label": "Acme Coffee — Suburb (— Subscription inactive)"
}
]
}GET /api/zapier/tags and GET /api/zapier/sources
Both return generic { id, label } items. Tags are the account's own review tags. Sources are a fixed list served from the API so a new source never requires a new app version.
{
"data": [
{ "id": "google", "label": "Google" },
{ "id": "facebook", "label": "Facebook" },
{ "id": "manual", "label": "Manual" },
{ "id": "pipeline_negative", "label": "Negative Pipeline Feedback" }
]
}Triggers (REST hooks)
Triggers are push-based REST hooks, not polling. A Zap subscribes once, and SyncMyReviews POSTs to the Zap's hook URL whenever the event occurs. Seven trigger keys are available.
| Trigger key | Name | Fires when |
|---|---|---|
new_review | New review | Any new Google, Facebook, or manual review on the selected business. |
new_low_rating_review | New low-rating review | A new review whose numeric rating is 3 or lower. |
new_five_star_review | New five-star review | A new review whose numeric rating is exactly 5. |
new_negative_pipeline_feedback | New negative pipeline feedback | A review-pipeline visitor submits a low rating into the private feedback branch. |
review_modified_upstream | Review modified upstream | A previously synced Google or Facebook review changes its text or rating upstream. |
qr_code_scanned | QR code scanned | One of the business's review QR codes is scanned. |
scheduled_post_outcome | Scheduled post outcome | A scheduled Google or Facebook post publishes or fails. |
A single new review can resolve to more than one trigger: a 1-star review fires both new_review and new_low_rating_review. A Facebook recommendation with no numeric rating fires only new_review, because a missing rating is never coerced into a rating band.
POST /api/zapier/triggers/{triggerKey}/subscriptions
Request body:
{
"business_id": 521,
"target_url": "https://hooks.zapier.com/hooks/catch/123/abc"
}target_url must be HTTPS and its host must sit under an allow-listed Zapier domain (zapier.com or zapierusercontent.com); anything else is rejected as a field-level validation error. The optional filters key is reserved for a future version and must be absent or empty. Responds 201:
{
"id": 9001,
"trigger_key": "new_review",
"business_id": 521,
"expires_at": null
}Subscribing is idempotent: repeating the same trigger key and target URL returns the existing subscription rather than creating a duplicate. Subscribing to a business whose subscription has lapsed is allowed on purpose — the Zap registers and simply stays quiet until billing is active again.
DELETE /api/zapier/triggers/{triggerKey}/subscriptions/{subscriptionId}
Soft-revokes the subscription and responds 204 with an empty body. An unknown id, or one owned by another account, returns 404 subscription_not_found.
GET /api/zapier/triggers/{triggerKey}/sample
Returns a single-element array holding one representative event for the trigger, so Zapier's test step always has data to map even on a brand-new account. Each item carries a top-level id for deduplication. An unrecognised trigger key returns 422 validation_failed.
Outbound payloads
Every outbound event shares one envelope. The data block is the only part that varies by trigger.
{
"_schema_version": 1,
"event": "new_review",
"event_id": "evt_01HXY9...",
"occurred_at": "2026-05-14T18:42:11Z",
"business": {
"id": 521,
"name": "Acme Coffee — Downtown",
"slug": "acme-coffee-downtown",
"timezone": "Europe/Belgrade"
},
"data": {
"source": "google",
"external_id": "AbF...xyz",
"rating": 5,
"rating_normalised_1_5": 5,
"review_text": "Best coffee in town.",
"author_name": "Jane Doe",
"posted_at": "2026-05-14T18:40:02Z",
"review_url": null,
"is_hidden": false,
"tags": []
}
}event_id is generated server-side and is stable, so a Zap can deduplicate on it. A few field-level details matter when you build downstream steps:
review_urlis optional. Facebook reviews carry a permalink; Google, manual, and pipeline reviews sendnull. Treat it as nullable, never as always present.ratingcan be null on Facebook. Recommendation-only reviews have no star rating, butrating_normalised_1_5is always populated (recommended maps to 5, not recommended maps to 1).- QR scan IPs are truncated. The full visitor IP never leaves the platform — it is reduced to a /24 prefix for IPv4 or a /48 prefix for IPv6.
- Pipeline submitter emails are never sent. They exist in SyncMyReviews but are excluded from every outbound payload.
- Post failures use a stable reason code.
failure_reasonis an enum rather than raw upstream error text, so provider error strings can never leak through. - Payloads are size-capped. Oversized events have their long text fields trimmed and flagged rather than being sent oversized.
Searches
Searches follow Zapier's conventions: results are a bare top-level array, and no match is an empty array with 200 — never a 404.
GET /api/zapier/searches/businesses?query=
Matches up to 25 businesses by case-insensitive partial name or by exact slug. Wildcard characters typed by the user are matched literally.
[
{ "id": 521, "name": "Acme Coffee — Downtown", "slug": "acme-coffee-downtown" }
]GET /api/zapier/searches/reviews?source=&external_id=
Returns zero or one match. source accepts google, facebook, or manual. Manual reviews have no upstream id, so that source always returns an empty array; pipeline_negative is not searchable and is rejected with 422.
[
{
"id": 88421,
"source": "google",
"external_id": "AbF...xyz",
"business_id": 521,
"rating": 5,
"review_text": "Best coffee in town.",
"author_name": "Jane Doe",
"is_hidden": false,
"posted_at": "2026-05-14T18:40:02Z"
}
]Actions
zapier:write ability, are rate-limited per token, and re-check the target business's subscription inside the same transaction as the write. An inactive business returns 402 subscription_inactive.POST /api/zapier/actions/manual-reviews
Records an external survey result as a manual review alongside Google and Facebook reviews.
| Field | Rules |
|---|---|
business_id | Required integer. |
author_name | Required string, max 200 characters. |
author_email | Optional email, max 200 characters. |
rating | Required integer between 1 and 5. |
review_text | Required string, max 5000 characters. |
Responds 201:
{
"review_id": 88421,
"source": "manual",
"business_id": 521,
"author_name": "Jane Doe",
"rating": 4,
"review_text": "Survey response imported from Typeform.",
"created_at": "2026-05-14T18:50:00+00:00"
}PATCH /api/zapier/actions/reviews/{reviewId}/visibility
Hides or shows a review across the public widgets, badges, and trust card. Requires source (google, facebook, or manual) and a boolean hidden. An unknown or cross-account review id returns 404 review_not_found.
{
"review_id": 88421,
"source": "google",
"business_id": 521,
"hidden": true,
"updated_at": "2026-05-14T18:52:10+00:00"
}POST /api/zapier/actions/google-posts
Queues a Google Business Profile post. The business must have a connected Google location, otherwise the call returns 409 missing_google_connection. Accepts business_id, an optional title (max 200), a required body (max 1500), optional image_url and cta_url, and a scheduled_for timestamp that must be in the future and within 90 days.
{
"scheduled_post_id": 412,
"platform": "google",
"business_id": 521,
"status": "scheduled",
"title": "Spring promotion",
"body": "Get 20% off all espresso drinks this week.",
"image_url": null,
"scheduled_for": "2026-05-20T15:00:00+00:00",
"created_at": "2026-05-14T18:55:00+00:00"
}POST /api/zapier/actions/facebook-posts
Same contract without title or cta_url. The business must have a connected Facebook page, otherwise the call returns 409 missing_facebook_connection. The response mirrors the Google shape with "platform": "facebook" and no title.
Limits and delivery
- Rate limiting. Write actions are limited per token, so one busy connection can never throttle another account. Exhausting the bucket returns
429 rate_limit_exceeded. - Signed deliveries. Every outbound POST carries an
X-Sync-Signatureheader of the formt=<unix-timestamp>,v1=<hex>, an HMAC-SHA256 over<timestamp>.<raw-body>. Signing keys rotate with a dual-key grace window. - Disabled Zaps clean themselves up. A
410 Gonefrom the hook URL automatically unsubscribes that hook. - Transient failures retry. A timeout,
429, or5xxfrom the hook URL is queued and retried with exponential backoff. After the retries are exhausted the subscription is flagged for review rather than deleted. - Subscription gate at delivery time. Billing is re-checked when the event is dispatched, not only when it was raised. If the business's subscription has lapsed the event is skipped silently and no webhook is sent.
- Duplicate suppression. Identical events raised within a short window for the same subscription are coalesced into a single delivery.
Get started
Account owners generate a Zapier token from Connections → Zapier in the SyncMyReviews dashboard (sign-in required). Members cannot mint tokens. Use the workflow builder below to connect SyncMyReviews to the rest of your stack without leaving this page.