Developers

Feedback Guru API

Ask customers for a review the moment a booking ends or an order ships. Your system calls one endpoint with the customer's name and email; we send the request from your business name, follow up on your schedule, and stop when they respond.

The API is included with Pro ($19/mo per location). You can sign up, set up a location and read everything here for free.

What you can do

  • ·Queue a review request with the customer's name, your own message, 1 to 3 emails, and when each one goes out.
  • ·Check each request: sent, opened, rated, clicked through to Google or Trustpilot.
  • ·Cancel a request before the next email.
  • ·Pull the star ratings and private notes customers leave, filtered by rating or date.
  • ·List your locations, and update names, review links, alerts, timezone, default schedule and logo.

Safe to retry: a repeat call for the same customer within 6 months returns 409 duplicate instead of a second email.

Documentation

REST over HTTPS, JSON in and out.

Getting started

Base URL: https://www.feedback-guru.com/api/v1. Send and receive JSON. Authenticate every request with your key:

curl https://www.feedback-guru.com/api/v1/locations \
  -H "Authorization: Bearer $FEEDBACK_GURU_API_KEY"

The API works once at least one location in your account is on Pro; until then calls return 403 plan_required. Review requests can be sent for Pro locations. Keys are for servers only. Don't put one in a web page or mobile app, where anyone could read it. You can make up to 120 requests per minute per key; past that you get a 429 with a Retry-After header.

Queue a review request

POST /api/v1/locations/{id}/review-requests (Pro locations)

curl -X POST https://www.feedback-guru.com/api/v1/locations/123/review-requests \
  -H "Authorization: Bearer $FEEDBACK_GURU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Jane",
    "last_name": "Smith",
    "email": "jane@example.com",
    "email_attempts": 2,
    "days_until_first_attempt": 1,
    "days_before_second_attempt": 4
  }'
FieldTypeDetails
emailstring, requiredThe customer's email address.
first_namestring, recommendedUsed in the greeting ("Hi Jane,"). Up to 100 characters. Without a plain first name the email says "Hi there".
last_namestring, optionalUp to 100 characters. Shown in your dashboard, not in the email.
messagestring, optionalReplaces our default text in the email body. Plain text, up to 1000 characters, line breaks kept, no links. The greeting, the "Rate your experience" button, your business name and address, and the unsubscribe link are always included. Without it we use our standard message.
email_attemptsinteger 1-3, default 1How many emails this customer can get: the request plus up to 2 reminders. Reminders stop as soon as they open their link.
days_until_first_attemptinteger 0-90, default 0Wait this many days before the first email. 0 means the next sending window.
days_before_second_attemptinteger 1-30Days between the first email and the first reminder. Defaults to the location's setting. Ignored when email_attempts is 1.
days_before_third_attemptinteger 1-30Days between the first and second reminder. Defaults to the location's setting. Ignored unless email_attempts is 3.

Returns 201 with the request (see the request object below).

  • One request per customer per location every 6 months. If this email address was added to this location in the last 6 months (from the API or the dashboard, any status), you get 409 duplicate with existing_request_id, and nothing is sent. Addresses are compared without case or surrounding spaces.
  • Unsubscribes are respected. Addresses that unsubscribed from this business, bounced, or marked an email as spam get 422 suppressed.
  • Consent. By calling this endpoint you confirm the person is your customer and agreed to hear from your business, the same confirmation as the dashboard's checkbox. Don't send purchased or rented lists.
  • Sending pace. Requests are queued, not sent instantly. We send at most 10 emails per location per day, between 9am and 6pm in the location's timezone, reminders first. A request whose day has come goes out in the next window with room.

The request object

{
  "data": {
    "id": 812,
    "location_id": 123,
    "status": "queued",
    "email": "jane@example.com",
    "first_name": "Jane",
    "last_name": "Smith",
    "name": "Jane Smith",
    "message": null,
    "source": "api",
    "email_attempts": 2,
    "emails_sent": 0,
    "schedule": {
      "days_until_first_attempt": 1,
      "days_before_second_attempt": 4,
      "days_before_third_attempt": null
    },
    "first_attempt_not_before": "2026-10-23T14:05:11Z",
    "created_at": "2026-10-22T14:05:11Z",
    "last_sent_at": null,
    "opened_at": null,
    "rated_at": null,
    "review_link_clicked_at": null,
    "ended_at": null
  }
}
FieldTypeDetails
statusstringqueued (not emailed yet), in_progress (emailed, more reminders possible), responded (opened their link), finished (all emails sent, no response), unsubscribed, bounced, complained (marked as spam), canceled.
opened_attimestampWhen they opened their rating link. Reminders stop at this point.
rated_attimestampWhen they left a rating on your rating page.
review_link_clicked_attimestampWhen they clicked through to your Google or Trustpilot review page. The review site doesn't tell us whether they posted.

All timestamps are UTC, ISO 8601.

List, look up and cancel requests

GET /api/v1/locations/{id}/review-requests, newest first. Optional query parameters: email, status, limit (1-100, default 50) and cursor (the next_cursor from the previous page; null on the last page).

curl "https://www.feedback-guru.com/api/v1/locations/123/review-requests?email=jane@example.com" \
  -H "Authorization: Bearer $FEEDBACK_GURU_API_KEY"

GET /api/v1/review-requests/{request_id} returns one request.

DELETE /api/v1/review-requests/{request_id} cancels a queued or in-progress request: no more emails go to that customer. The address still counts as asked for the next 6 months.

Ratings and notes

The star ratings and private notes customers leave on your rating page (from your QR code, link or review request emails). Reviews posted on Google or Trustpilot live on those sites and aren't included.

GET /api/v1/locations/{id}/reviews, newest first. Optional query parameters: min_rating and max_rating (1-5), since (a UTC date like 2026-10-01 or time like 2026-10-01T14:00:00Z), limit (1-100, default 50) and cursor.

curl "https://www.feedback-guru.com/api/v1/locations/123/reviews?max_rating=3&since=2026-10-01" \
  -H "Authorization: Bearer $FEEDBACK_GURU_API_KEY"
{
  "data": [
    {
      "id": 5120,
      "location_id": 123,
      "rating": 3,
      "note": "Great coffee, but we waited 20 minutes.",
      "device": "mobile",
      "created_at": "2026-10-20T16:42:09Z"
    }
  ],
  "next_cursor": null
}
FieldTypeDetails
ratinginteger 1-5The star rating.
notestring or nullThe private note, if they wrote one.
devicemobile, desktop or nullWhat they rated from.
created_attimestampWhen they rated, in UTC.

GET /api/v1/reviews/{review_id} returns one.

Free locations show their latest 5 ratings, as in the dashboard; the list then includes a limited object saying so. Pro locations show all of them.

Locations

GET /api/v1/locations lists your locations; GET /api/v1/locations/{id} returns one.

{
  "data": {
    "id": 123,
    "name": "Ember Cafe",
    "display_name": "Ember Cafe",
    "name_identifier": "ember-cafe-123",
    "feedback_url": "https://www.feedback-guru.com/feedback/ember-cafe-123",
    "plan": "pro",
    "review_platform": "Google",
    "review_url": "https://search.google.com/local/writereview?placeid=...",
    "google_place_id": "ChIJ...",
    "logo_url": "https://storage.googleapis.com/...",
    "email_alerts": { "enabled": true, "recipients": ["manager@example.com"] },
    "timezone": "America/New_York",
    "review_request_defaults": {
      "email_attempts": 3,
      "days_before_second_attempt": 3,
      "days_before_third_attempt": 4
    },
    "sending": { "paused": false, "paused_reason": null, "ready": true },
    "created_at": "2026-01-14T18:20:00Z"
  }
}

PATCH /api/v1/locations/{id} changes only the fields you send:

curl -X PATCH https://www.feedback-guru.com/api/v1/locations/123 \
  -H "Authorization: Bearer $FEEDBACK_GURU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"display_name": "Ember Cafe Downtown", "timezone": "America/Chicago"}'
FieldTypeDetails
display_namestringThe name on your rating page, 1-200 characters. Your feedback URL stays the same.
review_urlstringA Google review link (search.google.com, g.page) or a Trustpilot review page (trustpilot.com/evaluate/your-domain). Locations linked to a Google listing always use that listing's link.
email_alertsobject{"enabled": true|false, "recipients": ["a@example.com"]}: email alerts for new ratings, to up to 10 extra addresses.
timezonestringSending happens 9am-6pm here. One of: America/New_York, America/Chicago, America/Denver, America/Phoenix, America/Los_Angeles, America/Anchorage, Pacific/Honolulu, America/Toronto, America/Vancouver, Europe/London, Europe/Berlin, Asia/Jerusalem, Australia/Sydney.
review_request_defaultsobjectThe schedule for requests added in the dashboard, and the default waits for API requests: email_attempts (1-3), days_before_second_attempt and days_before_third_attempt (1, 2, 3, 4, 5, 6, 7, 10, 14 days). API requests still default to 1 email unless you send email_attempts.

Creating and deleting locations is only available in the dashboard.

PUT /api/v1/locations/{id}/logo sets the logo on your rating page. Send a file (PNG, JPEG, WebP or GIF, up to 5 MB) or the public URL of one. We resize it to at most 512px and remove any metadata.

curl -X PUT https://www.feedback-guru.com/api/v1/locations/123/logo \
  -H "Authorization: Bearer $FEEDBACK_GURU_API_KEY" \
  -F "file=@logo.png"

curl -X PUT https://www.feedback-guru.com/api/v1/locations/123/logo \
  -H "Authorization: Bearer $FEEDBACK_GURU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/logo.png"}'

DELETE /api/v1/locations/{id}/logo removes it; your rating page then shows your business's first letter.

Errors

Errors share one shape, with a stable code to check in your code and a message for people. Validation errors also name the field.

{
  "error": {
    "code": "duplicate",
    "message": "This customer was already asked by this location in the last 6 months, so we won't ask again yet.",
    "existing_request_id": 640
  }
}
StatusCodeMeaning
400invalid_json, missing_logoThe body isn't a JSON object, or the logo is missing.
401missing_api_key, invalid_api_keyNo key, a wrong key, or a revoked key.
403plan_required, pro_requiredThe account has no Pro location yet, or this location isn't on Pro (needed for review requests).
404not_foundNo such location or request in your account.
405not_supported, method_not_allowedFor example, creating or deleting a location.
409duplicateThis address was added to the location in the last 6 months.
409location_paused, location_not_readySending is paused, or we still need the business name and postal address. Fix it in the dashboard.
409not_cancelableThe request already ended.
413too_largeThe body is over 8 MB.
422invalid_*, unknown_field, suppressed, message_has_links, message_too_long, logo_url_unusable, has_google_listingA field didn't pass validation; see message and field.
429rate_limitedOver 120 requests per minute. Wait for Retry-After seconds.
5xxserver_error, storage_errorSomething failed on our side. Retrying is safe: a repeated request for the same customer returns 409 duplicate instead of a second email.

Free Google review link + QR code

Start free