{
  "openapi": "3.1.0",
  "info": {
    "title": "Review Advisor API",
    "version": "1.0",
    "description": "Read-only access to your Review Advisor data. Documentation: https://review-advisor.com/docs/api",
    "contact": {
      "name": "Review Advisor",
      "email": "hi@review-advisor.com",
      "url": "https://review-advisor.com/docs/api"
    }
  },
  "servers": [
    {
      "url": "https://api.review-advisor.com/api/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "The account the API key belongs to."
    },
    {
      "name": "Locations & cards",
      "description": "A location is one venue with its own license — a hotel, a restaurant, a museum. Every location has NFC cards (or stands, stickers) that guests tap, and links: the buttons guests see after tapping, such as “Review us on Google”."
    },
    {
      "name": "Clicks",
      "description": "A click is recorded every time a guest taps a button on the page that opens when they scan a card — for example “Review us on Google”. Use the raw list to copy every event into your own system, or the stats endpoint for ready-made totals."
    },
    {
      "name": "Review Guard",
      "description": "With Review Guard on, guests first rate their visit with a smiley. Happy guests go on to the review platforms; neutral and unhappy guests can leave a private comment for you instead. Every rating is stored as feedback."
    },
    {
      "name": "Surveys",
      "description": "Surveys you built in the app and every response guests submitted. A survey belongs to one location; responses remember the card the guest came from."
    },
    {
      "name": "Online reviews",
      "description": "Public reviews of your locations on Google and Tripadvisor, and the ratings of the competitors you track — as collected by Review Advisor's competitor monitoring. A location appears here once its own business profile is connected in the Competitors screen."
    }
  ],
  "paths": {
    "/account": {
      "get": {
        "operationId": "get-account",
        "tags": [
          "Account"
        ],
        "summary": "Get your account",
        "description": "Returns the company the key belongs to and how many locations it can read. Handy as a first call to check that your key works.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": 2,
                    "company_name": "Museum of Illusions",
                    "email": "operations@example.com",
                    "locations_count": 9
                  }
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/locations": {
      "get": {
        "operationId": "list-locations",
        "tags": [
          "Locations & cards"
        ],
        "summary": "List locations",
        "description": "All locations you can see in the app — the ones you own and the ones shared with you.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "name": "Museum of Illusions Zagreb",
                      "industry": "Museum",
                      "license_expires_at": "2027-09-25T10:00:00.000Z",
                      "review_guard_enabled": true,
                      "cards_count": 10,
                      "created_at": "2026-09-22T04:54:01.498Z"
                    },
                    {
                      "id": 2,
                      "name": "Museum of Illusions Split",
                      "industry": "Museum",
                      "license_expires_at": "2027-09-25T10:00:00.000Z",
                      "review_guard_enabled": false,
                      "cards_count": 5,
                      "created_at": "2026-09-22T04:54:41.485Z"
                    }
                  ]
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/locations/{id}": {
      "get": {
        "operationId": "get-location",
        "tags": [
          "Locations & cards"
        ],
        "summary": "Get a location",
        "description": "One location with its cards, its links (the buttons on the guest page) and its surveys.\n\nLinks can be grouped: a link with is_group: true is a folder, and the links inside it point to it with parent_id.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The location id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": {
                    "id": 1,
                    "name": "Museum of Illusions Zagreb",
                    "industry": "Museum",
                    "license_expires_at": "2027-09-25T10:00:00.000Z",
                    "review_guard_enabled": true,
                    "cards_count": 2,
                    "created_at": "2026-09-22T04:54:01.498Z",
                    "cards": [
                      {
                        "id": 14,
                        "location_id": 1,
                        "label": "Ticket desk",
                        "serial_number": "00100014",
                        "slug": "00100014-1790053069938",
                        "is_active": true,
                        "created_at": "2026-09-22T04:57:49.939Z"
                      },
                      {
                        "id": 15,
                        "location_id": 1,
                        "label": "Exit",
                        "serial_number": "00100015",
                        "slug": "00100015-1790053073002",
                        "is_active": true,
                        "created_at": "2026-09-22T04:57:53.003Z"
                      }
                    ],
                    "links": [
                      {
                        "id": 11,
                        "label": "Google",
                        "url": "https://search.google.com/local/writereview?placeid=…",
                        "platform": "google",
                        "is_review_platform": true,
                        "is_group": false,
                        "parent_id": null,
                        "position": 0
                      },
                      {
                        "id": 12,
                        "label": "Tripadvisor",
                        "url": "https://www.tripadvisor.com/…",
                        "platform": "tripadvisor",
                        "is_review_platform": true,
                        "is_group": false,
                        "parent_id": null,
                        "position": 1
                      }
                    ],
                    "surveys": [
                      {
                        "id": 3,
                        "title": "Visitor experience",
                        "is_active": true
                      }
                    ]
                  }
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/cards": {
      "get": {
        "operationId": "list-cards",
        "tags": [
          "Locations & cards"
        ],
        "summary": "List cards",
        "description": "Every card of your locations. The label is the name you gave the card in the app, the serial number is printed on the card.",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only data of this location. Leave out for all your locations.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": 14,
                      "location_id": 1,
                      "label": "Ticket desk",
                      "serial_number": "00100014",
                      "slug": "00100014-1790053069938",
                      "is_active": true,
                      "created_at": "2026-09-22T04:57:49.939Z"
                    }
                  ]
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/clicks": {
      "get": {
        "operationId": "list-clicks",
        "tags": [
          "Clicks"
        ],
        "summary": "List clicks",
        "description": "Every click, one row per tap, oldest first.\n\nClicks count toward the location the card belongs to now. If you move a card to another location in the app, its earlier clicks move with it.",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only data of this location. Leave out for all your locations.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "card_id",
            "in": "query",
            "required": false,
            "description": "Only data collected through this card.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "link_id",
            "in": "query",
            "required": false,
            "description": "Only clicks on this link (button).",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Start of the period, inclusive. A date (2026-10-01, from midnight UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "End of the period, inclusive. A date (2026-10-31, until 23:59:59 UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page, 1–500.",
            "schema": {
              "type": "integer",
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The next_cursor from the previous page. Leave out for the first page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": 4122,
                      "clicked_at": "2026-10-26T09:24:50.648Z",
                      "location_id": 1,
                      "card": {
                        "id": 14,
                        "label": "Ticket desk"
                      },
                      "link": {
                        "id": 11,
                        "label": "Google",
                        "platform": "google"
                      }
                    },
                    {
                      "id": 4123,
                      "clicked_at": "2026-10-26T10:02:36.796Z",
                      "location_id": 1,
                      "card": {
                        "id": 15,
                        "label": "Exit"
                      },
                      "link": {
                        "id": 12,
                        "label": "Tripadvisor",
                        "platform": "tripadvisor"
                      }
                    }
                  ],
                  "pagination": {
                    "has_more": true,
                    "next_cursor": "4123"
                  }
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/stats/clicks": {
      "get": {
        "operationId": "click-stats",
        "tags": [
          "Clicks"
        ],
        "summary": "Click statistics",
        "description": "Click totals for a period, grouped by day, location, card or link. Days are calendar days in UTC; days without clicks are left out.\n\ngroup_by=location returns { location_id, name, clicks } for every location, including those with 0 clicks.\n\ngroup_by=card returns { card_id, label, location_id, clicks }; group_by=link returns { link_id, label, platform, location_id, clicks }. Both are sorted by clicks, highest first.",
        "parameters": [
          {
            "name": "group_by",
            "in": "query",
            "required": false,
            "description": "How to group the totals.",
            "schema": {
              "type": "string",
              "enum": [
                "day",
                "location",
                "card",
                "link"
              ],
              "default": "day"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only data of this location. Leave out for all your locations.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "card_id",
            "in": "query",
            "required": false,
            "description": "Only data collected through this card.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Start of the period, inclusive. A date (2026-10-01, from midnight UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "End of the period, inclusive. A date (2026-10-31, until 23:59:59 UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "date": "2026-10-09",
                      "clicks": 6
                    },
                    {
                      "date": "2026-10-10",
                      "clicks": 43
                    },
                    {
                      "date": "2026-10-11",
                      "clicks": 30
                    }
                  ],
                  "meta": {
                    "group_by": "day",
                    "from": "2026-10-01T00:00:00.000Z",
                    "to": "2026-10-31T23:59:59.999Z",
                    "total_clicks": 79
                  }
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/review-guard/feedback": {
      "get": {
        "operationId": "list-feedback",
        "tags": [
          "Review Guard"
        ],
        "summary": "List feedback",
        "description": "Every smiley rating, with the guest's comment when they left one. Oldest first.",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only data of this location. Leave out for all your locations.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "card_id",
            "in": "query",
            "required": false,
            "description": "Only data collected through this card.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "mood",
            "in": "query",
            "required": false,
            "description": "Only ratings with this mood.",
            "schema": {
              "type": "string",
              "enum": [
                "happy",
                "neutral",
                "unhappy"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Start of the period, inclusive. A date (2026-10-01, from midnight UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "End of the period, inclusive. A date (2026-10-31, until 23:59:59 UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page, 1–500.",
            "schema": {
              "type": "integer",
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The next_cursor from the previous page. Leave out for the first page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": 812,
                      "location_id": 1,
                      "card": {
                        "id": 14,
                        "label": "Ticket desk"
                      },
                      "mood": "happy",
                      "comment": null,
                      "created_at": "2026-10-26T09:24:51.014Z"
                    },
                    {
                      "id": 813,
                      "location_id": 1,
                      "card": {
                        "id": 15,
                        "label": "Exit"
                      },
                      "mood": "unhappy",
                      "comment": "The queue at the entrance was too long.",
                      "created_at": "2026-10-26T11:40:12.371Z"
                    }
                  ],
                  "pagination": {
                    "has_more": false,
                    "next_cursor": null
                  }
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/review-guard/summary": {
      "get": {
        "operationId": "feedback-summary",
        "tags": [
          "Review Guard"
        ],
        "summary": "Feedback summary",
        "description": "Ratings per location for a period: how many guests were happy, neutral and unhappy, and the share of happy guests. happy_percent is null when a location has no ratings.",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only data of this location. Leave out for all your locations.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Start of the period, inclusive. A date (2026-10-01, from midnight UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "End of the period, inclusive. A date (2026-10-31, until 23:59:59 UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "location_id": 1,
                      "name": "Museum of Illusions Zagreb",
                      "total": 240,
                      "happy": 221,
                      "neutral": 12,
                      "unhappy": 7,
                      "happy_percent": 92.1
                    },
                    {
                      "location_id": 2,
                      "name": "Museum of Illusions Split",
                      "total": 0,
                      "happy": 0,
                      "neutral": 0,
                      "unhappy": 0,
                      "happy_percent": null
                    }
                  ],
                  "meta": {
                    "from": "2026-10-01T00:00:00.000Z",
                    "to": "2026-10-31T23:59:59.999Z"
                  }
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/surveys": {
      "get": {
        "operationId": "list-surveys",
        "tags": [
          "Surveys"
        ],
        "summary": "List surveys",
        "description": "Your surveys with their questions. Use the question ids to match the answers in responses.\n\nQuestion types: rating (1–5 stars), scale (numbers, e.g. 0–10, see the scale object), choice (one option), multi (several options), text (free text).\n\nis_review_guard_survey: true marks the survey Review Guard sends neutral and unhappy guests to.",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only data of this location. Leave out for all your locations.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": 3,
                      "location_id": 1,
                      "title": "Visitor experience",
                      "description": null,
                      "is_active": true,
                      "is_review_guard_survey": false,
                      "responses_count": 418,
                      "questions": [
                        {
                          "id": 21,
                          "text": "How would you rate your visit?",
                          "type": "rating",
                          "required": true,
                          "position": 0,
                          "options": null
                        },
                        {
                          "id": 22,
                          "text": "How likely are you to recommend us to a friend?",
                          "type": "scale",
                          "required": true,
                          "position": 1,
                          "options": null,
                          "scale": {
                            "min": 0,
                            "max": 10,
                            "allow_na": false,
                            "min_label": "Not likely",
                            "max_label": "Very likely"
                          }
                        },
                        {
                          "id": 23,
                          "text": "Which rooms did you enjoy most?",
                          "type": "multi",
                          "required": false,
                          "position": 2,
                          "options": [
                            "Vortex Tunnel",
                            "Ames Room",
                            "Infinity Room"
                          ],
                          "allow_other": true,
                          "max_select": null
                        },
                        {
                          "id": 24,
                          "text": "Anything we could do better?",
                          "type": "text",
                          "required": false,
                          "position": 3,
                          "options": null
                        }
                      ],
                      "created_at": "2026-09-25T08:12:44.120Z"
                    }
                  ]
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/survey-responses": {
      "get": {
        "operationId": "list-survey-responses",
        "tags": [
          "Surveys"
        ],
        "summary": "List survey responses",
        "description": "Every submitted response with its answers, oldest first. Answers come typed: a number for rating and scale questions, an array of strings for multi, a string for choice and text.\n\nAn “Other” answer the guest typed in is returned as \"Other: <their text>\". A scale question that allows it can be answered \"N/A\". card is null when the guest opened the survey link directly instead of tapping a card.",
        "parameters": [
          {
            "name": "survey_id",
            "in": "query",
            "required": false,
            "description": "Only responses to this survey.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only data of this location. Leave out for all your locations.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "card_id",
            "in": "query",
            "required": false,
            "description": "Only data collected through this card.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Start of the period, inclusive. A date (2026-10-01, from midnight UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "End of the period, inclusive. A date (2026-10-31, until 23:59:59 UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page, 1–500.",
            "schema": {
              "type": "integer",
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The next_cursor from the previous page. Leave out for the first page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": 5120,
                      "survey_id": 3,
                      "survey_title": "Visitor experience",
                      "location_id": 1,
                      "card": {
                        "id": 15,
                        "label": "Exit"
                      },
                      "submitted_at": "2026-10-26T14:03:27.551Z",
                      "answers": [
                        {
                          "question_id": 21,
                          "question": "How would you rate your visit?",
                          "type": "rating",
                          "value": 5
                        },
                        {
                          "question_id": 22,
                          "question": "How likely are you to recommend us to a friend?",
                          "type": "scale",
                          "value": 9
                        },
                        {
                          "question_id": 23,
                          "question": "Which rooms did you enjoy most?",
                          "type": "multi",
                          "value": [
                            "Vortex Tunnel",
                            "Other: The gift shop"
                          ]
                        },
                        {
                          "question_id": 24,
                          "question": "Anything we could do better?",
                          "type": "text",
                          "value": "More benches in the last room."
                        }
                      ]
                    }
                  ],
                  "pagination": {
                    "has_more": false,
                    "next_cursor": null
                  }
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/reviews": {
      "get": {
        "operationId": "list-reviews",
        "tags": [
          "Online reviews"
        ],
        "summary": "List reviews",
        "description": "Reviews of your own locations, in the order we collected them. from and to filter on the date the review was published.",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only data of this location. Leave out for all your locations.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "description": "Only reviews from this platform.",
            "schema": {
              "type": "string",
              "enum": [
                "google",
                "tripadvisor"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Start of the period, inclusive. A date (2026-10-01, from midnight UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "End of the period, inclusive. A date (2026-10-31, until 23:59:59 UTC) or an ISO-8601 datetime.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page, 1–500.",
            "schema": {
              "type": "integer",
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The next_cursor from the previous page. Leave out for the first page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": 1990,
                      "location_id": 1,
                      "source": "google",
                      "external_id": "Ci9DQUlRQUNvZENodHljRjlvT2tWTVdqUm1URjg0Y1ZrM1ZuRkxhVnB0TVZWa1NXYxAB",
                      "rating": 5,
                      "text": "Loved it, the kids had a great time!",
                      "author": "Ana K.",
                      "published_at": "2026-10-17T10:40:36.000Z"
                    }
                  ],
                  "pagination": {
                    "has_more": true,
                    "next_cursor": "1990"
                  }
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/competitors": {
      "get": {
        "operationId": "list-competitors",
        "tags": [
          "Online reviews"
        ],
        "summary": "List ratings & competitors",
        "description": "The current Google and Tripadvisor rating of each location (is_self: true) and of the competitors you track next to it.",
        "parameters": [
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only data of this location. Leave out for all your locations.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "id": 4,
                      "location_id": 1,
                      "name": "Museum of Illusions Zagreb",
                      "address": "Ilica 72, 10000 Zagreb",
                      "is_self": true,
                      "google": {
                        "place_id": "ChIJ…",
                        "rating": 4.7,
                        "total_reviews": 12840,
                        "synced_at": "2026-10-27T02:00:53.848Z"
                      },
                      "tripadvisor": {
                        "url": "https://www.tripadvisor.com/…",
                        "rating": 4.5,
                        "total_reviews": 3120,
                        "synced_at": "2026-10-27T02:05:10.002Z"
                      }
                    },
                    {
                      "id": 5,
                      "location_id": 1,
                      "name": "Another attraction",
                      "address": "…",
                      "is_self": false,
                      "google": {
                        "place_id": "ChIJ…",
                        "rating": 4.3,
                        "total_reviews": 4267,
                        "synced_at": "2026-10-27T02:01:34.183Z"
                      },
                      "tripadvisor": null
                    }
                  ]
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key from Account settings → API Access."
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "enum": [
                        "invalid_parameter",
                        "missing_api_key",
                        "invalid_api_key",
                        "api_access_disabled",
                        "not_found",
                        "rate_limited",
                        "server_error"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "code",
                    "message"
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}