{"openapi":"3.1.0","info":{"title":"OneTouchReview API","version":"1.0","summary":"Send review requests and read your reviews, requests and stats.","description":"The OneTouchReview API lets your own systems send review requests and read\nyour Google reviews, reviews from other sites, private feedback, review\nrequests and stats. **API access is part of Pro.**\n\n## Sign in\nMake an API key in the app (Settings → Connections → API keys). Send it as\na bearer token on every call:\n\n```\nAuthorization: Bearer rev_…\n```\n\nEach key is **Full access** (sends review requests and reads) or **Read\nonly** (reads only). A read-only key on `POST /hooks/requests` gets\n`403 API_KEY_READ_ONLY`. Access is chosen when the key is made and can't be\nchanged: make a new key instead.\n\nSenders that can't set headers can post review requests to the key's\n**secret URL** instead (`POST /hooks/requests/{urlToken}`, a `whk_…` token\nshown once when the key is made). Treat that URL like a password.\n\n## Signed calls (optional)\nEach key has a signing secret (`whsec_…`). When you send the\n`X-Rev-Signature` header we check it, and a key can be set to require it:\n\n```\nX-Rev-Signature: t={unix seconds},v1={hex HMAC-SHA256(signing_secret, \"{t}.{raw body}\")}\n```\n\n`t` must be within 300 seconds of our clock, and each signature is\naccepted once.\n\n## Limits\n120 calls a minute per key. Every answer has `X-RateLimit-Limit` and\n`X-RateLimit-Remaining`; a `429` adds `Retry-After` (seconds).\n\n## Errors\nErrors have one shape: `{ \"error\": { \"code\": \"…\", \"message\": \"…\" } }`\n(validation errors add `details`, field → messages).\n\n## Paging\nLists that page return `next_cursor`; pass it back as `cursor` for the\nnext page. It is `null` on the last page. Treat cursors as opaque.\n\n## Privacy\nClients' phone numbers and emails are never returned by the read\nendpoints below.\n","license":{"name":"Proprietary","identifier":"LicenseRef-Proprietary"},"contact":{"name":"OneTouchReview support","url":"https://onetouchreview.com/contact"},"x-zapier-url":"https://zapier.com/developer/public-invite/247183/648d2866fd6ff95581cd074a79ddb7a5/"},"servers":[{"url":"https://api.onetouchreview.com/api/v1"}],"tags":[{"name":"Review requests","description":"Send a review request, and read the ones you sent."},{"name":"Reviews","description":"Google reviews, reviews from other sites and private feedback."},{"name":"Account","description":"Your business, locations, staff and stats."},{"name":"Zapier","description":"REST hook subscriptions (used by the Zapier app; any client may use them)."}],"security":[{"apiKey":[]}],"x-signed-url":"`POST /hooks/requests/{urlToken}` signs in by the key's secret URL token\n(`whk_…`) in the path, no header. OpenAPI has no scheme for a token in the\npath, so that operation has `security: []` and `x-auth: signed-url`.\n","x-rate-limit-headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"x-example-private-feedback":{"id":3301,"rating":3,"private_feedback":"The wait was long, but the staff were kind.","customer_name":"Sam Lee","customer_phone":"+15125550188","location_id":12,"location_name":"Downtown","review_request_id":9120,"external_id":"case-0998","responded_at":"2026-10-12T19:44:00.000000Z"},"x-example-assistant-lead":{"id":"b118","name":"Alex Moore","phone":"+15125550142","email":"alex@example.com","card":{"id":9,"kind":"business","name":"Downtown"},"location":{"id":12,"name":"Downtown"},"topic":"Consultation","question":"Do you handle car accidents?","booking":{"service":"Free consultation","requested_at":"2026-10-15T15:00:00+00:00","note":"Afternoons are best"},"conversation_url":"https://app.example.com/assistant/conversations/5521","channel":"card","page_url":null,"created_at":"2026-10-13T09:12:00.000000Z"},"paths":{"/hooks/requests":{"post":{"operationId":"sendReviewRequest","tags":["Review requests"],"summary":"Send a review request","x-access":"full","description":"Adds the client (found by phone, else by email when there is no phone)\nand sends them a review request. Sending the same `external_id` again\nreturns the first request and sends nothing (`duplicate: true`, `200`).\nSenders may use their own field names: `first_name` + `last_name` →\n`name`; `mobile`, `mobile_phone`, `cell`, `phone_number` → `phone`;\n`email_address` → `email`; `id`, `job_id`, `case_id`, `matter_id` →\n`external_id`. Ours win when both are sent.\n\nAdd `\"test\": true` to run every check without keeping or sending\nanything.\n\nNeeds a **full-access** key. Every client gets the same request, and\nonly clients who agreed to receive texts may be sent by text.\n","requestBody":{"$ref":"#/components/requestBodies/ReviewRequestInput"},"responses":{"201":{"$ref":"#/components/responses/RequestAccepted"},"200":{"$ref":"#/components/responses/RequestDuplicateOrTest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/ForbiddenWrite"},"404":{"$ref":"#/components/responses/LocationNotFound"},"422":{"$ref":"#/components/responses/SendRefused"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"listReviewRequests","tags":["Review requests"],"summary":"List review requests","x-access":"read_only","description":"Review requests (follow-ups included), newest first, 50 a page.","parameters":[{"$ref":"#/components/parameters/LocationId"},{"name":"status","in":"query","description":"Only requests in this status.","schema":{"type":"string","enum":["queued","sent","delivered","read","failed","expired","cancelled"]}},{"name":"from","in":"query","description":"Only requests made on or after this day (UTC, `YYYY-MM-DD`).","schema":{"type":"string","format":"date"}},{"name":"to","in":"query","description":"Only requests made on or before this day (UTC, `YYYY-MM-DD`).","schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of review requests.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["requests","next_cursor"],"properties":{"requests":{"type":"array","items":{"$ref":"#/components/schemas/ReviewRequest"}},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; null on the last page."}}},"example":{"requests":[{"id":9123,"status":"delivered","channel":"sms","location_id":12,"customer":{"id":5531,"name":"Nina Patel"},"sent_at":"2026-10-12T15:02:11.000000Z","delivered_at":"2026-10-12T15:02:14.000000Z","clicked_at":"2026-10-12T16:40:03.000000Z","created_at":"2026-10-12T15:02:10.000000Z"}],"next_cursor":"9123"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/LocationNotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/requests/{urlToken}":{"post":{"operationId":"sendReviewRequestBySecretUrl","tags":["Review requests"],"summary":"Send a review request (secret URL)","x-access":"full","x-auth":"signed-url","description":"The same as `POST /hooks/requests`, signed in by the key's secret URL\ninstead of the `Authorization` header — for senders that can't set\nheaders. The URL is shown once when the key is made; make a new one in\nthe app if it leaks. Needs a **full-access** key.\n","security":[],"parameters":[{"name":"urlToken","in":"path","required":true,"description":"The key's secret URL token (`whk_…`).","schema":{"type":"string","example":"whk_3fZK…"}}],"requestBody":{"$ref":"#/components/requestBodies/ReviewRequestInput"},"responses":{"201":{"$ref":"#/components/responses/RequestAccepted"},"200":{"$ref":"#/components/responses/RequestDuplicateOrTest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/ForbiddenWrite"},"404":{"$ref":"#/components/responses/LocationNotFound"},"422":{"$ref":"#/components/responses/SendRefused"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/requests/{id}":{"get":{"operationId":"getReviewRequest","tags":["Review requests"],"summary":"Get one review request","x-access":"read_only","description":"One request, in the list's shape, plus `not_sent_reason` when it\ndidn't go (failed, cancelled or expired; otherwise null). Another\nbusiness's request is `404`.\n","parameters":[{"name":"id","in":"path","required":true,"description":"The review request id.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The review request.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ReviewRequest"},{"type":"object","required":["not_sent_reason"],"properties":{"not_sent_reason":{"oneOf":[{"type":"null"},{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"For example OPTED_OUT, NO_CONSENT, RECENTLY_REQUESTED, QUOTA_EXCEEDED, SMS_NOT_REGISTERED, LOCATION_PAUSED, CLICKED, CANCELLED, EXPIRED, SEND_FAILED."},"message":{"type":"string"}}}]}}}]},"example":{"id":9124,"status":"failed","channel":"sms","location_id":12,"customer":{"id":5532,"name":"Sam Lee"},"sent_at":null,"delivered_at":null,"clicked_at":null,"created_at":"2026-10-12T15:05:00.000000Z","not_sent_reason":{"code":"OPTED_OUT","message":"The client opted out of messages."}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/me":{"get":{"operationId":"me","tags":["Account"],"summary":"Check the key","x-access":"read_only","description":"The business and key name. Zapier uses it to test the connection.","responses":{"200":{"description":"The key works.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["business_name","key_name"],"properties":{"business_name":{"type":"string"},"key_name":{"type":"string"}}},"example":{"business_name":"Patel Law","key_name":"Zapier"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/locations":{"get":{"operationId":"listLocations","tags":["Account"],"summary":"List locations","x-access":"read_only","description":"The business's locations, for `location_id`.","responses":{"200":{"description":"The locations.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["locations"],"properties":{"locations":{"type":"array","items":{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer"},"name":{"type":"string"}}}}}},"example":{"locations":[{"id":12,"name":"Downtown"},{"id":14,"name":"Northside"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/staff":{"get":{"operationId":"listStaff","tags":["Account"],"summary":"List staff","x-access":"read_only","description":"Active staff members, for `staff_member_id` on a review request.","responses":{"200":{"description":"The staff.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["staff"],"properties":{"staff":{"type":"array","items":{"type":"object","required":["id","name","location_id","location_name"],"properties":{"id":{"type":"integer"},"name":{"type":"string"},"location_id":{"type":"integer"},"location_name":{"type":["string","null"]}}}}}},"example":{"staff":[{"id":77,"name":"Maria Gomez","location_id":12,"location_name":"Downtown"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/stats":{"get":{"operationId":"getStats","tags":["Account"],"summary":"Ratings and request counts","x-access":"read_only","description":"For each location: Google's rating and review count, each other review\nsite's, and the review requests sent in the last 30 days and how many\nof those were clicked.\n","parameters":[{"$ref":"#/components/parameters/LocationId"}],"responses":{"200":{"description":"The stats.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["locations"],"properties":{"locations":{"type":"array","items":{"type":"object","required":["id","name","google","sites","requests_last_30_days"],"properties":{"id":{"type":"integer"},"name":{"type":"string"},"google":{"type":"object","properties":{"rating":{"type":["number","null"]},"count":{"type":["integer","null"]}}},"sites":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string"},"rating":{"type":["number","null"]},"count":{"type":["integer","null"]}}}},"requests_last_30_days":{"type":"object","properties":{"sent":{"type":"integer"},"clicked":{"type":"integer"}}}}}}}},"example":{"locations":[{"id":12,"name":"Downtown","google":{"rating":4.8,"count":212},"sites":[{"platform":"avvo","rating":4.9,"count":37},{"platform":"yelp","rating":4.5,"count":18}],"requests_last_30_days":{"sent":64,"clicked":29}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/LocationNotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/google-reviews":{"get":{"operationId":"listGoogleReviews","tags":["Reviews"],"summary":"List Google reviews","x-access":"read_only","description":"The newest 50 Google reviews. Reviews no longer on Google are left out\nunless `include_removed=1`; each review then has `removed_at`.\n","parameters":[{"name":"include_removed","in":"query","description":"`1` to include reviews no longer on Google (each then has `removed_at`).","schema":{"type":"integer","enum":[0,1]}}],"responses":{"200":{"description":"The reviews.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/GoogleReview"}}}},"example":{"items":[{"id":4410,"rating":5,"comment":"Quick, kind and clear. Thank you!","reviewer_name":"Nina P.","location_id":12,"location_name":"Downtown","reply":"Thank you, Nina — we're glad we could help.","created_at":"2026-10-11T18:20:00.000000Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/site-reviews":{"get":{"operationId":"listSiteReviews","tags":["Reviews"],"summary":"List reviews from other sites","x-access":"read_only","description":"Reviews from the other sites you connected (Avvo, Yelp, Facebook, BBB\nand more), newest posted first, 50 a page. Reviewers are shown by\nfirst name, as on the site.\n","parameters":[{"name":"platform","in":"query","description":"Only this site, for example `avvo`, `yelp`, `facebook`, `bbb`, `findlaw`, `justia`.","schema":{"type":"string"}},{"$ref":"#/components/parameters/LocationId"},{"name":"since","in":"query","description":"Only reviews posted on or after this day (`YYYY-MM-DD`).","schema":{"type":"string","format":"date"}},{"$ref":"#/components/parameters/Cursor"}],"responses":{"200":{"description":"A page of reviews.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["reviews","next_cursor"],"properties":{"reviews":{"type":"array","items":{"$ref":"#/components/schemas/SiteReview"}},"next_cursor":{"type":["string","null"]}}},"example":{"reviews":[{"id":801,"platform":"avvo","location_id":12,"rating":5,"title":"Excellent lawyer","body":"She explained every step and got a great result.","reviewer_name":"James","posted_on":"2026-10-09","owner_response":null,"review_url":"https://www.avvo.com/attorneys/…/reviews.html"}],"next_cursor":"MjAyNi0xMC0wOXw4MDE"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/LocationNotFound"},"422":{"$ref":"#/components/responses/ValidationFailed"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/private-feedback":{"get":{"operationId":"listPrivateFeedback","tags":["Reviews"],"summary":"List private feedback","x-access":"read_only","description":"The newest 50 written answers clients left on your review page\n(the \"New private feedback\" Zapier trigger).\n","responses":{"200":{"description":"The feedback.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PrivateFeedback"}}}},"example":{"items":[{"id":3301,"rating":3,"private_feedback":"The wait was long, but the staff were kind.","customer_name":"Sam Lee","customer_phone":"+15125550188","location_id":12,"location_name":"Downtown","review_request_id":9120,"external_id":"case-0998","responded_at":"2026-10-12T19:44:00.000000Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/assistant-leads":{"get":{"operationId":"listAssistantLeads","tags":["Reviews"],"summary":"List leads from the card assistant","x-access":"read_only","description":"The newest 50 details forms and booking requests from the card assistant.","responses":{"200":{"description":"The leads.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AssistantLead"}}}},"example":{"items":[{"id":"b118","name":"Alex Moore","phone":"+15125550142","email":"alex@example.com","card":{"id":9,"kind":"business","name":"Downtown"},"location":{"id":12,"name":"Downtown"},"topic":"Consultation","question":"Do you handle car accidents?","booking":{"service":"Free consultation","requested_at":"2026-10-15T15:00:00+00:00","note":"Afternoons are best"},"conversation_url":"https://app.example.com/assistant/conversations/5521","channel":"card","page_url":null,"created_at":"2026-10-13T09:12:00.000000Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/subscriptions":{"get":{"operationId":"listSubscriptions","tags":["Zapier"],"summary":"List webhook subscriptions","x-access":"read_only","description":"This key's subscriptions, newest first.","responses":{"200":{"description":"The subscriptions.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"type":"object","required":["subscriptions"],"properties":{"subscriptions":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"last_delivered_at":{"type":["string","null"],"format":"date-time"},"last_status":{"type":["integer","null"],"description":"HTTP status of the last try; 0 = no answer."}}}]}}}},"example":{"subscriptions":[{"id":31,"event":"google_review.created","target_url":"https://hooks.example.com/rev","created_at":"2026-10-01T09:00:00.000000Z","last_delivered_at":"2026-10-11T18:21:02.000000Z","last_status":200}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createSubscription","tags":["Zapier"],"summary":"Subscribe to an event","x-access":"read_only","description":"We POST each new event to `target_url` (a public https address); see\n**Webhooks**. Up to 25 subscriptions a key. Works with either kind of key.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["event","target_url"],"properties":{"event":{"type":"string","enum":["private_feedback.created","google_review.created","assistant_lead"]},"target_url":{"type":"string","format":"uri","maxLength":2000}}},"example":{"event":"google_review.created","target_url":"https://hooks.example.com/rev"}}}},"responses":{"201":{"description":"Subscribed.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"},"example":{"id":32,"event":"google_review.created","target_url":"https://hooks.example.com/rev","created_at":"2026-10-13T10:00:00.000000Z"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"description":"Not valid, or `TOO_MANY_SUBSCRIPTIONS`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"TOO_MANY_SUBSCRIPTIONS","message":"This API key already has 25 subscriptions. Turn off a Zap or use another key."}}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/hooks/subscriptions/{id}":{"delete":{"operationId":"deleteSubscription","tags":["Zapier"],"summary":"Unsubscribe","x-access":"read_only","description":"Stops the deliveries at once.","parameters":[{"name":"id","in":"path","required":true,"description":"The subscription id.","schema":{"type":"integer"}}],"responses":{"204":{"description":"Unsubscribed.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"webhooks":{"private_feedback.created":{"post":{"operationId":"privateFeedbackCreated","summary":"New private feedback","description":"A client left written feedback on your review page. Delivered to each\nsubscription for this event. See **Delivery** under `google_review.created`.\n","parameters":[{"$ref":"#/components/parameters/DeliveryEvent"},{"$ref":"#/components/parameters/DeliveryId"},{"$ref":"#/components/parameters/DeliverySignature"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrivateFeedback"},"example":{"id":3301,"rating":3,"private_feedback":"The wait was long, but the staff were kind.","customer_name":"Sam Lee","customer_phone":"+15125550188","location_id":12,"location_name":"Downtown","review_request_id":9120,"external_id":"case-0998","responded_at":"2026-10-12T19:44:00.000000Z"}}}},"responses":{"200":{"description":"Received (any 2xx)."},"410":{"description":"Unsubscribe; we delete the subscription."}}}},"google_review.created":{"post":{"operationId":"googleReviewCreated","summary":"New Google review","description":"A new Google review (left in the last 7 days) was saved.\n\n**Delivery.** We POST the item as JSON with `X-Rev-Event` (the event),\n`X-Rev-Delivery` (a unique id per delivery; the same on retries is not\nguaranteed, dedupe by the item's `id`) and, when the key has a signing\nsecret, `X-Rev-Signature: t={unix seconds},v1={hex HMAC-SHA256(signing_secret, \"{t}.{raw body}\")}`.\nCheck it by computing the HMAC over `\"{t}.{raw body}\"` with your\nsigning secret, comparing in constant time, and refusing a `t` more\nthan 300 seconds old. Any 2xx is delivered; 410 deletes the\nsubscription; anything else (or no answer in 10 s) is tried again\nafter 60, 300 and 900 seconds.\n","parameters":[{"$ref":"#/components/parameters/DeliveryEvent"},{"$ref":"#/components/parameters/DeliveryId"},{"$ref":"#/components/parameters/DeliverySignature"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoogleReview"},"example":{"id":4410,"rating":5,"comment":"Quick, kind and clear. Thank you!","reviewer_name":"Nina P.","location_id":12,"location_name":"Downtown","reply":null,"created_at":"2026-10-11T18:20:00.000000Z"}}}},"responses":{"200":{"description":"Received (any 2xx)."},"410":{"description":"Unsubscribe; we delete the subscription."}}}},"assistant_lead":{"post":{"operationId":"assistantLead","summary":"New lead from the card assistant","description":"A visitor filled in the details form or asked for a booking.","parameters":[{"$ref":"#/components/parameters/DeliveryEvent"},{"$ref":"#/components/parameters/DeliveryId"},{"$ref":"#/components/parameters/DeliverySignature"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantLead"},"example":{"id":"b118","name":"Alex Moore","phone":"+15125550142","email":"alex@example.com","card":{"id":9,"kind":"business","name":"Downtown"},"location":{"id":12,"name":"Downtown"},"topic":"Consultation","question":"Do you handle car accidents?","booking":{"service":"Free consultation","requested_at":"2026-10-15T15:00:00+00:00","note":"Afternoons are best"},"conversation_url":"https://app.example.com/assistant/conversations/5521","channel":"card","page_url":null,"created_at":"2026-10-13T09:12:00.000000Z"}}}},"responses":{"200":{"description":"Received (any 2xx)."},"410":{"description":"Unsubscribe; we delete the subscription."}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"rev_…","description":"An API key from Settings → Connections → API keys: `Authorization: Bearer rev_…`.\nFull-access keys send review requests and read; read-only keys only read.\n"}},"headers":{"XRateLimitLimit":{"description":"Calls allowed a minute for this key (120).","schema":{"type":"integer"}},"XRateLimitRemaining":{"description":"Calls left this minute.","schema":{"type":"integer"}}},"parameters":{"LocationId":{"name":"location_id","in":"query","description":"Only this location (from `GET /hooks/locations`). Not one of yours → 404 LOCATION_NOT_FOUND.","schema":{"type":"integer"}},"Cursor":{"name":"cursor","in":"query","description":"The `next_cursor` of the previous page.","schema":{"type":"string"}},"DeliveryEvent":{"name":"X-Rev-Event","in":"header","description":"The event, for example `google_review.created`.","schema":{"type":"string"}},"DeliveryId":{"name":"X-Rev-Delivery","in":"header","description":"A unique id for this delivery (UUID).","schema":{"type":"string","format":"uuid"}},"DeliverySignature":{"name":"X-Rev-Signature","in":"header","description":"`t={unix seconds},v1={hex HMAC-SHA256(signing_secret, \"{t}.{raw body}\")}`; sent when the key has a signing secret.","schema":{"type":"string"}}},"requestBodies":{"ReviewRequestInput":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":255,"description":"The client's name."},"phone":{"type":"string","maxLength":20,"description":"US mobile number. Needed unless `email` is sent."},"email":{"type":"string","format":"email","maxLength":255,"description":"Needed unless `phone` is sent."},"external_id":{"type":"string","maxLength":100,"description":"Your id for the job or case; the same id again sends nothing."},"event_at":{"type":"string","format":"date-time","description":"When the job was done (not in the future); the send is timed from it."},"location_id":{"type":"integer","description":"The location (from `GET /hooks/locations`)."},"staff_member_id":{"type":"integer","description":"An active staff member at that location (from `GET /hooks/staff`)."},"staff_name":{"type":"string","maxLength":255,"description":"Or the staff member's name; unknown names add the warning STAFF_NOT_FOUND."},"case_type":{"type":"string","maxLength":100},"send_now":{"type":"boolean","description":"Send at once instead of at the usual time."},"channel":{"type":"string","enum":["auto","sms","email"]},"source":{"type":"string","enum":["api","zapier"]},"language":{"type":"string","maxLength":50,"description":"For example \"Spanish\" or \"es\"; unknown values are ignored."},"test":{"type":"boolean","description":"Run every check; keep and send nothing."}}},"example":{"name":"Nina Patel","phone":"(512) 555-0123","external_id":"case-1001","location_id":12,"staff_name":"Maria Gomez"}}}}},"responses":{"RequestAccepted":{"description":"Accepted; the request is queued.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestAccepted"},"example":{"id":9125,"status":"queued","sends_at":"2026-10-13T15:00:00.000000Z","customer_id":5531,"duplicate":false,"warnings":[]}}}},"RequestDuplicateOrTest":{"description":"The same `external_id` was sent before (nothing sent), or a test call.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RequestAccepted"},{"type":"object","properties":{"test":{"type":"boolean","const":true},"would_send":{"type":"boolean"},"sends_at":{"type":"string","format":"date-time"},"customer_id":{"type":["integer","null"]},"duplicate":{"type":"boolean"},"warnings":{"type":"array","items":{"type":"string"}}}}]},"examples":{"duplicate":{"value":{"id":9125,"status":"queued","sends_at":"2026-10-13T15:00:00.000000Z","customer_id":5531,"duplicate":true,"warnings":[]}},"test":{"value":{"test":true,"would_send":true,"sends_at":"2026-10-13T15:00:00.000000Z","customer_id":null,"duplicate":false,"warnings":[]}}}}}},"Unauthorized":{"description":"The key is missing, wrong or revoked, or the signature is wrong.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"key":{"value":{"error":{"code":"INVALID_API_KEY","message":"The API key is missing, wrong or revoked."}}},"signature":{"value":{"error":{"code":"INVALID_SIGNATURE","message":"The X-Rev-Signature header is missing, wrong, too old or already used."}}}}}}},"SubscriptionRequired":{"description":"The account has no active subscription.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"SUBSCRIPTION_REQUIRED","message":"A valid subscription is required to continue."}}}}},"Forbidden":{"description":"The plan doesn't include the API (Pro), or the account is suspended.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"PLAN_REQUIRED","message":"The API is part of Pro. Upgrade to use it."}}}}},"ForbiddenWrite":{"description":"A read-only key, the plan doesn't include the API (Pro), or the account is suspended.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"readOnly":{"value":{"error":{"code":"API_KEY_READ_ONLY","message":"This key can only read. Use a full-access key to send review requests."}}},"plan":{"value":{"error":{"code":"PLAN_REQUIRED","message":"The API is part of Pro. Upgrade to use it."}}}}}}},"NotFound":{"description":"Not found, or not this business's.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"NOT_FOUND","message":"Resource not found."}}}}},"LocationNotFound":{"description":"`location_id` is not one of your locations.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"LOCATION_NOT_FOUND","message":"Location not found."}}}}},"ValidationFailed":{"description":"A parameter is not valid.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"VALIDATION_FAILED","message":"The selected status is invalid.","details":{"status":["The selected status is invalid."]}}}}}},"SendRefused":{"description":"Not valid (`VALIDATION_FAILED`), or not sent, with the reason:\n`RECENTLY_REQUESTED`, `CUSTOMER_OPTED_OUT`, `NO_CONSENT`, `NO_CHANNEL`,\n`QUOTA_EXHAUSTED`, `SMS_NOT_REGISTERED`, `LOCATION_PAUSED`,\n`NO_REVIEW_LINK`, …\n","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"RECENTLY_REQUESTED","message":"Asked for a review recently. You can ask again on Jan 2, 2027."}}}}},"RateLimited":{"description":"More than 120 calls this minute. Wait `Retry-After` seconds.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"},"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"RATE_LIMITED","message":"Too many calls. Wait 42 seconds and try again."}}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}}},"RequestAccepted":{"type":"object","required":["id","status","sends_at","customer_id","duplicate","warnings"],"properties":{"id":{"type":"integer"},"status":{"type":"string"},"sends_at":{"type":["string","null"],"format":"date-time"},"customer_id":{"type":"integer"},"duplicate":{"type":"boolean"},"warnings":{"type":"array","items":{"type":"string","enum":["STAFF_NOT_FOUND"]}}}},"ReviewRequest":{"type":"object","required":["id","status","channel","location_id","customer","sent_at","delivered_at","clicked_at","created_at"],"properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["queued","sent","delivered","read","failed","expired","cancelled"]},"channel":{"type":["string","null"],"enum":["sms","email","auto",null]},"location_id":{"type":["integer","null"]},"customer":{"description":"Name only; never the phone number or email.","oneOf":[{"type":"null"},{"type":"object","required":["id","name"],"properties":{"id":{"type":"integer"},"name":{"type":"string"}}}]},"sent_at":{"type":["string","null"],"format":"date-time"},"delivered_at":{"type":["string","null"],"format":"date-time"},"clicked_at":{"type":["string","null"],"format":"date-time","description":"When the client opened the review link."},"created_at":{"type":"string","format":"date-time"}}},"GoogleReview":{"type":"object","required":["id","rating","comment","reviewer_name","location_id","location_name","reply","created_at"],"properties":{"id":{"type":"integer"},"rating":{"type":["integer","null"],"minimum":1,"maximum":5},"comment":{"type":["string","null"]},"reviewer_name":{"type":["string","null"]},"location_id":{"type":["integer","null"]},"location_name":{"type":["string","null"]},"reply":{"type":["string","null"],"description":"Your reply on Google."},"created_at":{"type":["string","null"],"format":"date-time","description":"When the review was left."},"removed_at":{"type":["string","null"],"format":"date-time","description":"Only with `include_removed=1`: when we saw it was gone from Google."}}},"SiteReview":{"type":"object","required":["id","platform","location_id","rating","title","body","reviewer_name","posted_on","owner_response","review_url"],"properties":{"id":{"type":"integer"},"platform":{"type":"string"},"location_id":{"type":["integer","null"]},"rating":{"type":["integer","null"],"description":"Facebook: null (recommends / doesn't recommend)."},"title":{"type":["string","null"]},"body":{"type":["string","null"]},"reviewer_name":{"type":["string","null"],"description":"First name, as on the site."},"posted_on":{"type":["string","null"],"format":"date"},"owner_response":{"type":["string","null"]},"review_url":{"type":["string","null"],"format":"uri"}}},"PrivateFeedback":{"type":"object","properties":{"id":{"type":"integer"},"rating":{"type":["integer","null"]},"private_feedback":{"type":"string"},"customer_name":{"type":["string","null"]},"customer_phone":{"type":["string","null"],"description":"Kept for existing Zaps."},"location_id":{"type":["integer","null"]},"location_name":{"type":["string","null"]},"review_request_id":{"type":"integer"},"external_id":{"type":["string","null"]},"responded_at":{"type":["string","null"],"format":"date-time"}}},"AssistantLead":{"type":"object","properties":{"id":{"type":"string","description":"\"c{conversation}\" (details form) or \"b{booking}\" (booking request)."},"name":{"type":["string","null"]},"phone":{"type":["string","null"]},"email":{"type":["string","null"]},"card":{"type":["object","null"]},"location":{"type":["object","null"]},"topic":{"type":["string","null"]},"question":{"type":["string","null"]},"booking":{"type":["object","null"]},"conversation_url":{"type":"string","format":"uri"},"channel":{"type":"string"},"page_url":{"type":["string","null"]},"created_at":{"type":["string","null"],"format":"date-time"}}},"Subscription":{"type":"object","required":["id","event","target_url","created_at"],"properties":{"id":{"type":"integer"},"event":{"type":"string","enum":["private_feedback.created","google_review.created","assistant_lead"]},"target_url":{"type":"string","format":"uri"},"created_at":{"type":"string","format":"date-time"}}}}}}