{"openapi":"3.0.3","info":{"title":"Repuso public API","version":"1.5.0","description":"Build your own integrations on top of Repuso: manage sub accounts and users, send and track review invitations, read and curate reviews, and pull the same reports the dashboard shows.\n\n## Authentication\n\nGenerate an API key in your Repuso dashboard under **Integrations → API**. Keys can be created for the main account or for a specific sub account - every API call is automatically scoped to the sub account the key belongs to.\n\nAuthenticate with **HTTP Basic auth**: leave the username empty and use your API key as the password.\n\n```\ncurl https://api.repuso.com/public/v1/hello -u \":YOUR_API_KEY\"\n```\n\n## Rate limits\n\nShort bursts are throttled per key (a few requests per 10 seconds). Space out bulk operations or use the bulk endpoints.\n\n## Content type\n\nSend and receive JSON. `POST` and `PUT` requests must include the header `Content-Type: application/json`.","contact":{"name":"Repuso support","url":"https://repuso.com/contact/","email":"hello@repuso.com"}},"servers":[{"url":"https://api.repuso.com/public/v1"}],"security":[{"basicAuth":[]}],"tags":[{"name":"Connection","description":"Test your API key."},{"name":"Reviews","description":"Read, create and curate the reviews of the sub account your API key belongs to. Approving a review publishes it to your widgets immediately."},{"name":"Channels","description":"Connect and manage the review platforms (Google, Facebook, Yelp, Tripadvisor…) that reviews are collected from. Platforms connect in different ways - check `GET /channels/types` for per-platform instructions. Reviews are collected by background crawlers: connecting a channel queues the first crawl, and refreshes are queued too - nothing crawls inline."},{"name":"Review invitations","description":"Send review requests by email or SMS and track everything that happens to them. Follow-up sequences send up to 3 automatic reminder emails; a click, unsubscribe, failure or review on any step cancels the remaining steps."},{"name":"Invite settings","description":"Invitation delay, templates, follow-up sequence, cooldown and send-window configuration."},{"name":"Reports","description":"The same numbers the dashboard shows, ready for your own reporting."},{"name":"Account","description":"Account info and sub account management for white label / agency integrations. Requires an API key with account-management permission."},{"name":"Users","description":"User management for white label / agency integrations. Requires an API key with account-management permission."},{"name":"Widgets","description":"Create and manage embeddable review widgets without the dashboard.\n\nSettings are **per widget type** - there is no universal settings schema. `GET /widgets/types` returns every type with its full default settings object; any subset of those keys can be sent in `settings` on create/update and is merged recursively over the defaults. Keys you don't send keep their defaults.\n\nThe embed snippets returned by `GET /widgets/get/{id}` render from the widgets CDN - paste the `html` snippet anywhere, or use `advanced` (separate head script + custom element tag), or the WordPress shortcode. Widgets show the sub-account's approved reviews; curate via the Reviews endpoints."},{"name":"Webhooks","description":"Receive signed HTTP POST notifications when things happen in your account - no polling needed. Events fire for reviews collected from **any** connected platform.\n\n### Events\n| Event | Fired when |\n|---|---|\n| `review.created` | A new review is collected from any platform |\n| `rating.changed` | A monitored profile's official score or review count changes |\n| `profile.error` | A monitored profile stops being crawlable (first failure or disable) |\n\n### Delivery\nEach delivery is a `POST` with a JSON envelope:\n\n```json\n{\n  \"id\": \"evt_293060\",\n  \"event\": \"review.created\",\n  \"occurred_on\": \"2026-08-06T14:37:32Z\",\n  \"data\": { ... }\n}\n```\n\nRespond with any `2xx` status within 10 seconds. Failed deliveries are retried on a backoff schedule (5 minutes, then hourly, then daily) before the delivery is dropped.\n\n### Verifying signatures\nEvery delivery includes an `X-Repuso-Signature` header:\n\n```\nX-Repuso-Signature: t=1754491052,v1=5f8a...\n```\n\n`v1` is `HMAC-SHA256(\"<t>.<raw body>\", secret)` using the `whsec_...` secret returned once when the webhook was created. To verify: concatenate the `t` value, a period, and the raw request body; compute the HMAC with your secret; compare to `v1` with a constant-time comparison. Reject deliveries whose timestamp is older than your tolerance (e.g. 5 minutes) to prevent replays."},{"name":"Properties","description":"Properties group a sub-account's review profiles (e.g. by location or rental unit). For historical reasons these are managed via the /groups endpoints and the group_id field on profile update."}],"paths":{"/hello":{"get":{"tags":["Connection"],"summary":"Test connection","description":"Returns a success payload when your API key is valid and the API integration is enabled.","responses":{"200":{"description":"Connection OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/reviews/get":{"get":{"tags":["Reviews"],"summary":"List reviews","description":"Returns the reviews of the sub account your API key belongs to - filterable by curation status, star rating, platform and more. Returns up to 100 per call; paginate by passing both the `next_before_ts` and `next_before_id` cursors from the response back as `before_ts` and `before_id` (keyset, newest first).","parameters":[{"name":"rating","in":"query","description":"Star ratings to include, comma separated (1-5). Ratings on other scales are normalized to 5 stars.","schema":{"type":"string","example":"4,5"}},{"name":"channels","in":"query","description":"Channel ids, comma separated. Combine with `sources` to match either.","schema":{"type":"string"}},{"name":"sources","in":"query","description":"Custom source ids, comma separated.","schema":{"type":"string"}},{"name":"tags","in":"query","description":"Tag ids, comma separated.","schema":{"type":"string"}},{"name":"q","in":"query","description":"Searches review text and author names.","schema":{"type":"string"}},{"name":"media_only","in":"query","description":"Only reviews with photos or videos.","schema":{"type":"integer","enum":[1]}},{"name":"with_photos","in":"query","description":"Only reviews with an author photo.","schema":{"type":"integer","enum":[1]}},{"name":"limit","in":"query","description":"Page size, 1-100 (default 25).","schema":{"type":"integer","default":25,"minimum":1,"maximum":100}},{"name":"min_rating","in":"query","description":"Only reviews with a star rating at or above this value (1-5). Ratings on other scales are normalized to 5 stars.","schema":{"type":"number","minimum":1,"maximum":5,"example":4}},{"name":"max_rating","in":"query","description":"Only reviews with a star rating at or below this value (1-5). Ratings on other scales are normalized to 5 stars.","schema":{"type":"number","minimum":1,"maximum":5}},{"name":"since","in":"query","description":"Only reviews posted on or after this date. Accepts a date or datetime, e.g. `2026-01-01` or `2026-01-01 09:00:00`.","schema":{"type":"string","example":"2026-01-01"}},{"name":"until","in":"query","description":"Only reviews posted on or before this date. Accepts a date or datetime, e.g. `2026-06-30` or `2026-06-30 23:59:59`.","schema":{"type":"string"}},{"name":"translate","in":"query","description":"ISO language code, e.g. `es`. Returns each review's `text` translated to this language; the untranslated text is kept in `text_original` and the target code in `text_language`.","schema":{"type":"string","example":"es"}},{"name":"before_ts","in":"query","description":"Pagination cursor - pass the `next_before_ts` of the previous page to fetch the next one. Pass together with `before_id`.","schema":{"type":"integer"}},{"name":"before_id","in":"query","description":"Pagination cursor - pass the `next_before_id` of the previous page alongside `before_ts` to fetch the next one. Both are required together to break ties between reviews with the same timestamp.","schema":{"type":"integer"}}],"responses":{"200":{"description":"A page of reviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/reviews/get/{id}":{"get":{"tags":["Reviews"],"summary":"Get a review","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"The review","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Review"}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reviews/add":{"post":{"tags":["Reviews"],"summary":"Add a review","description":"Creates a review collected outside Repuso. Approving it (`status: 1`) publishes it to your widgets immediately. Duplicates (same author + text) are rejected.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewCreate"}}}},"responses":{"200":{"description":"The created review","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"integer"},"review":{"$ref":"#/components/schemas/Review"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/reviews/add_bulk":{"post":{"tags":["Reviews"],"summary":"Add reviews in bulk","description":"Validates and inserts many reviews at once. With `validate_only: true` the payload is checked but nothing is inserted.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"validate_only":{"type":"boolean","default":false},"reviews":{"type":"array","items":{"$ref":"#/components/schemas/ReviewCreate"}}},"required":["reviews"]}}}},"responses":{"200":{"description":"Validation / insert results with `valid` and `invalid` counts and per-item `errors`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkResult"}}}}}}},"/reviews/update/{id}":{"put":{"tags":["Reviews"],"summary":"Update a review","description":"Update content, curation status or rating. Approving purges widget caches automatically.","parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","description":"`inbox`, `approved`, `ignored` or 0-2.","example":"approved"},"text":{"type":"string"},"from_name":{"type":"string"},"posted_on":{"type":"string","format":"date"},"rating_value":{"type":"integer","minimum":0,"maximum":5},"from_image":{"type":"string","format":"uri"},"post_url":{"type":"string","format":"uri"},"video_url":{"type":"string","format":"uri","description":"Attach a YouTube / Vimeo / direct video."},"source_id":{"type":"integer","description":"Custom source id - only for reviews that don't come from a channel."},"tags":{"type":"array","items":{"type":"string"},"description":"Replaces ALL tags of the review with these tag names; unknown names are created automatically. Returned as {id, name} objects. Filter reviews by tag ids via the tags query parameter on the list endpoint.","example":["vip","summer-campaign"]}}}}}},"responses":{"200":{"description":"The updated review","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"review":{"$ref":"#/components/schemas/Review"}}}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reviews/delete/{id}":{"delete":{"tags":["Reviews"],"summary":"Delete a review","description":"Deletes the review and purges widget caches.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/get":{"get":{"tags":["Channels"],"summary":"List channels","description":"Connected channels of the token's sub account. Responses are cached for a few minutes; mutations invalidate the cache.","responses":{"200":{"description":"Connected channels","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/Channel"}}}}}}}}}},"/channels/get/{id}":{"get":{"tags":["Channels"],"summary":"Get a channel","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"The channel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Channel"}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/types":{"get":{"tags":["Channels"],"summary":"List connectable platforms","description":"The catalog of platforms Repuso can collect reviews from, each with its connection method and required parameters. Platforms connect in different ways: most take a public profile `url`, a few use OAuth (`authorize_url` flow), and some are dashboard-only.","responses":{"200":{"description":"Platform catalog","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/ChannelType"}}}}}}}}}},"/channels/types/{type}":{"get":{"tags":["Channels"],"summary":"Get a platform's connection info","description":"One platform with its connection instructions and, where relevant, platform metadata such as selectable stores and countries (App Store, Google Play, Yelp, Tripadvisor…).","parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string","example":"google"}}],"responses":{"200":{"description":"The platform","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelType"}}}},"404":{"description":"Unknown platform type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/check":{"post":{"tags":["Channels"],"summary":"Check before connecting","description":"Validates the platform type and your plan's channel limits without connecting anything. Returns 402 when the sub account is at its per-platform channel limit.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","example":"yelp"}},"required":["type"]}}}},"responses":{"200":{"description":"OK to connect","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"402":{"description":"Plan limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/add":{"post":{"tags":["Channels"],"summary":"Connect a channel","description":"Connects a platform. URL platforms take a public profile `url` (for WordPress: the URL of your site with the Repuso plugin installed); Delighted takes your Delighted API key as `access_token`. Details are validated, then the first review crawl is queued in the background - reviews appear once it completes. OAuth platforms (Facebook, Instagram, Twitter/X, Zendesk) return an error pointing to the `authorize_url` flow. Requires an admin or editor API key.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","description":"Platform type slug, see `/channels/types`.","example":"google"},"url":{"type":"string","format":"uri","description":"Public profile / listing URL on the platform (url-method platforms)."},"access_token":{"type":"string","description":"The platform's API key (credentials-method platforms, e.g. Delighted)."},"trend_id":{"type":"string","description":"Delighted only - collect a specific trend."},"stores":{"type":"string","description":"App Store / Google Play / Yelp / Tripadvisor only - see the type's metadata."},"countries":{"type":"string","description":"Google Play only - see the type's metadata."}},"required":["type"]}}}},"responses":{"200":{"description":"Connected, first crawl queued","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"crawl_queued":{"type":"boolean","example":true},"channel":{"$ref":"#/components/schemas/Channel"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"402":{"description":"Plan limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The URL could not be validated as a profile on this platform","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/authorize_url":{"post":{"tags":["Channels"],"summary":"Get an OAuth authorize URL","description":"For OAuth platforms (Facebook, Instagram, Twitter/X, Zendesk): returns the authorization URL. Send the account owner through it in a browser - the connection completes after they approve access. Zendesk additionally requires `subdomain`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","example":"facebook"},"subdomain":{"type":"string","description":"Zendesk only."}},"required":["type"]}}}},"responses":{"200":{"description":"The authorize URL","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"type":{"type":"string"},"authorize_url":{"type":"string","format":"uri"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/channels/update/{id}":{"put":{"tags":["Channels"],"summary":"Update a channel","description":"Editable fields: `name`, `auto_approve` (new reviews go straight to approved), `auto_approve_4_stars`, and `stores` / `countries` for store-based platforms. Requires an admin or editor API key.","parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"auto_approve":{"type":"boolean"},"auto_approve_4_stars":{"type":"boolean"},"stores":{"type":"string"},"countries":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"channel":{"$ref":"#/components/schemas/Channel"}}}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/delete/{id}":{"delete":{"tags":["Channels"],"summary":"Disconnect a channel","description":"Disconnects the platform. By default its collected reviews are removed too - pass `keep_reviews=1` to keep them. Requires an admin or editor API key.","parameters":[{"$ref":"#/components/parameters/id"},{"name":"keep_reviews","in":"query","description":"`1` - keep the reviews collected from this channel.","schema":{"type":"integer","enum":[1]}}],"responses":{"200":{"description":"Disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"reviews_kept":{"type":"boolean"}}}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/refresh/{id}":{"post":{"tags":["Channels"],"summary":"Queue a refresh","description":"Queues a background re-crawl of the channel. Crawls never run inline: channels refresh automatically on a regular schedule, so this is only needed after changes on the platform side. Allowed once per channel per 6 hours - recently-checked channels return 429 with `retry_after_minutes`.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"202":{"description":"Refresh queued","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"queued":{"type":"boolean","example":true},"msg":{"type":"string"}}}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Checked recently or already queued","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"queued":{"type":"boolean","example":false},"msg":{"type":"string"},"last_checked":{"type":"string"},"retry_after_minutes":{"type":"integer"}}}}}}}}},"/invite/requests/get":{"get":{"tags":["Review invitations"],"summary":"List requests","parameters":[{"name":"status","in":"query","description":"One or more statuses, comma separated (e.g. `pending,sent`).","schema":{"type":"string"}},{"name":"status_not","in":"query","description":"Exclude statuses, comma separated.","schema":{"type":"string"}},{"name":"search","in":"query","description":"Searches recipient, name and data.","schema":{"type":"string"}},{"name":"recipient","in":"query","description":"Exact recipient match.","schema":{"type":"string"}},{"name":"parent_id","in":"query","description":"List the follow-up steps of a specific request.","schema":{"type":"integer"}},{"name":"type","in":"query","description":"`email` or `sms`.","schema":{"type":"string","enum":["email","sms"]}},{"name":"start","in":"query","description":"Offset for paging.","schema":{"type":"integer"}},{"name":"limit","in":"query","schema":{"type":"integer"}},{"name":"order","in":"query","description":"Sort column, default `id`.","schema":{"type":"string"}},{"name":"sort","in":"query","schema":{"type":"string","enum":["ASC","DESC"]}},{"$ref":"#/components/parameters/settings_id"}],"responses":{"200":{"description":"A page of requests","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/InviteRequest"}}}}}}}}}},"/invite/requests/get/{id}":{"get":{"tags":["Review invitations"],"summary":"Get a request","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"The request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRequest"}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/invite/requests/add":{"post":{"tags":["Review invitations"],"summary":"Create a request","description":"Queues a review invitation. Without an explicit delay the settings' default delay applies. When the settings' follow-up sequence is enabled, follow-up steps are scheduled automatically for email requests.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRequestCreate"}}}},"responses":{"200":{"description":"The created request","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"integer"},"request":{"$ref":"#/components/schemas/InviteRequest"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/invite/requests/add_bulk":{"post":{"tags":["Review invitations"],"summary":"Create requests in bulk","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"validate_only":{"type":"boolean","default":false},"requests":{"type":"array","items":{"$ref":"#/components/schemas/InviteRequestCreate"}},"settings_id":{"type":"integer"}},"required":["requests"]}}}},"responses":{"200":{"description":"Validation / insert results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkResult"}}}}}}},"/invite/requests/send/{id}":{"post":{"tags":["Review invitations"],"summary":"Send a request now","description":"Sends a pending request immediately instead of waiting for its schedule.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Sent","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"request":{"$ref":"#/components/schemas/InviteRequest"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/invite/requests/update/{id}":{"put":{"tags":["Review invitations"],"summary":"Update a request","description":"Editable fields: `name`, `recipient`, `schedule`, `data`.","parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"recipient":{"type":"string"},"schedule":{"type":"string","example":"2026-08-10 10:00:00"},"data":{"type":"object"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/invite/requests/delete/{id}":{"delete":{"tags":["Review invitations"],"summary":"Delete a request","description":"Deleting a request also cancels its remaining follow-up steps.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/invite/requests/activity/{id}":{"get":{"tags":["Review invitations"],"summary":"Request activity timeline","description":"Per-request event timeline: queued, sent, opened, clicked, unsubscribed, reviewed…","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"The timeline","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"request":{"type":"object","description":"Basic request info."},"activity":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"created_at":{"type":"string"},"source":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"}}}}}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/invite/requests/reviewed/{id}":{"put":{"tags":["Review invitations"],"summary":"Mark as reviewed","description":"Marks a request as reviewed and cancels its remaining follow-up steps.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Marked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/invite/requests/stop_sequence/{id}":{"post":{"tags":["Review invitations"],"summary":"Stop follow-ups","description":"Cancels the pending follow-up steps of a request's sequence without touching the request itself.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Stopped","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"stopped_steps":{"type":"integer"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/invite/settings/get":{"get":{"tags":["Invite settings"],"summary":"Get settings","parameters":[{"$ref":"#/components/parameters/settings_id"}],"responses":{"200":{"description":"The settings","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"settings":{"$ref":"#/components/schemas/InviteSettings"}}}}}}}}},"/invite/settings/list":{"get":{"tags":["Invite settings"],"summary":"List settings profiles","responses":{"200":{"description":"All settings profiles of this sub account","content":{"application/json":{"example":{"success":true,"settings":[{"id":34,"name":"Default","from_name":"Joe's Pizza","subject":"How was your visit?","language":"en","channels":[512],"followups":2,"default":true}]}}}}}}},"/invite/settings/add":{"post":{"tags":["Invite settings"],"summary":"Create a settings profile","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"$ref":"#/components/schemas/InviteSettings"}},"required":["settings"]}}}},"responses":{"200":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}}}}},"/invite/settings/update/{settings_id}":{"put":{"tags":["Invite settings"],"summary":"Update a settings profile","parameters":[{"name":"settings_id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"$ref":"#/components/schemas/InviteSettings"}},"required":["settings"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}}}}},"/invite/settings/delete/{settings_id}":{"delete":{"tags":["Invite settings"],"summary":"Delete a settings profile","parameters":[{"name":"settings_id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}}}}},"/invite/templates/list":{"get":{"tags":["Invite settings"],"summary":"List email templates","responses":{"200":{"description":"Available email templates","content":{"application/json":{"example":{"success":true,"templates":[{"id":1,"name":"Clean","preview":"https://repuso.com/images/invite-templates/clean.png"},{"id":2,"name":"Boxed","preview":"https://repuso.com/images/invite-templates/boxed.png"}]}}}}}}},"/invite/templates/get/{slug}":{"get":{"tags":["Invite settings"],"summary":"Get a rendered email template","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"faces"}}],"responses":{"200":{"description":"The rendered template HTML","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"html":{"type":"string"}}}}}}}}},"/reports/ratings":{"get":{"tags":["Reports"],"summary":"Ratings summary","description":"Per-platform rating summary: official score, review count and channel breakdown for every connected platform, plus collected reviews and a combined weighted total.","responses":{"200":{"description":"The summary","content":{"application/json":{"example":{"success":true,"types":{"9":{"label":"Google","logo":"https://widgets.repuso.net/2.0/images/60x60/logo-google.png","rating_scale":5,"official_score":4.7,"official_num_reviews":214,"channel_count":1,"rating_value":4.7,"channels":[{"id":512,"name":"Joe's Pizza","official_score":4.7,"official_num_reviews":214}]},"11":{"label":"Tripadvisor","logo":"https://widgets.repuso.net/2.0/images/60x60/logo-tripadvisor.png","rating_scale":5,"official_score":4.5,"official_num_reviews":96,"channel_count":1,"rating_value":4.5,"channels":[]}},"totals":{"rating_value":4.64,"official_num_reviews":310}}}}}}}},"/reports/reviews_over_time":{"get":{"tags":["Reports"],"summary":"Reviews over time","description":"Reviews per month with platform and star-rating distributions - the dashboard chart data.","parameters":[{"name":"range","in":"query","description":"Months to include, default 12.","schema":{"type":"integer","default":12}},{"name":"type","in":"query","description":"Narrow to one platform type id.","schema":{"type":"integer"}},{"name":"channel_id","in":"query","description":"Narrow to one channel.","schema":{"type":"integer"}}],"responses":{"200":{"description":"Labels, datasets and distributions","content":{"application/json":{"example":{"success":true,"labels":["9/2025","10/2025","11/2025"],"datasets":{"Google":[12,18,9],"Tripadvisor":[3,5,4],"total":[15,23,13]},"distributions":{"1":1,"2":1,"3":4,"4":14,"5":31}}}}}}}},"/account":{"get":{"tags":["Account"],"summary":"Get account","description":"Account info, plan and usage, including invitation credits.","responses":{"200":{"description":"The account","content":{"application/json":{"example":{"created_on":"2024-11-02 09:12:44","company_name":"Acme Agency","website":"https://acme.example","vanity_url":"acme","properties":3,"video":1,"invite":1,"invite_credits":240,"plan":"Agency","sub_accounts":12}}}}}}},"/account/subaccounts":{"get":{"tags":["Account"],"summary":"List sub accounts","responses":{"200":{"description":"All sub accounts","content":{"application/json":{"example":[{"id":21,"name":"Client One","vanity":"client-one","properties":3,"video":true,"invite":1,"image":"https://api.repuso.com/media/logos/21.png","channels":4},{"id":22,"name":"Client Two","vanity":"client-two","properties":1,"video":false,"invite":1,"image":"","channels":2}]}}}}}},"/account/subaccount":{"post":{"tags":["Account"],"summary":"Create a sub account","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"properties":{"type":"integer","description":"Max channels per platform type."}},"required":["name"]}}}},"responses":{"200":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/account/subaccount/{id}":{"put":{"tags":["Account"],"summary":"Update a sub account","parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}}}},"delete":{"tags":["Account"],"summary":"Delete a sub account","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}}}}},"/users/get":{"get":{"tags":["Users"],"summary":"List users","responses":{"200":{"description":"All users of this account","content":{"application/json":{"example":[{"id":731,"email":"owner@acme.example","name":"Alex Owner","role_id":1,"sub_accounts":[]},{"id":794,"email":"client@client-one.example","name":"Client User","role_id":2,"sub_accounts":[21]}]}}}}}},"/users":{"post":{"tags":["Users"],"summary":"Create a user","description":"Requires an admin API key. `sub_accounts` limits the user to specific sub accounts.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"},"name":{"type":"string"},"sub_accounts":{"type":"array","items":{"type":"integer"}}},"required":["email","password"]}}}},"responses":{"200":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"400":{"$ref":"#/components/responses/ValidationError"}}}},"/users/{id}":{"get":{"tags":["Users"],"summary":"Get a user","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"The user","content":{"application/json":{"example":{"id":794,"email":"client@client-one.example","name":"Client User","role_id":2,"sub_accounts":[21]}}}}}},"put":{"tags":["Users"],"summary":"Update a user","parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"password":{"type":"string"},"sub_accounts":{"type":"array","items":{"type":"integer"}}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}}}},"delete":{"tags":["Users"],"summary":"Delete a user","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}}}}},"/login/get_url":{"post":{"tags":["Users"],"summary":"Get a login URL for a user","description":"Generates a direct login URL for a user - ideal for embedding the (white labeled) dashboard behind your own authentication.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}},"required":["email","password"]}}}},"responses":{"200":{"description":"Login URLs","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"url":{"type":"string","description":"Login URL on your white label domain when configured."},"alternate_url":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/webhooks/get":{"get":{"tags":["Webhooks"],"summary":"List webhooks","responses":{"200":{"description":"All webhooks on the account (including dashboard-created ones, marked `format: legacy`)","content":{"application/json":{"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}}}}}}},"/webhooks/get/{id}":{"get":{"tags":["Webhooks"],"summary":"Get a webhook","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"The webhook","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/add":{"post":{"tags":["Webhooks"],"summary":"Create a webhook","description":"The response includes the signing `secret` - it is returned only this once.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookInput"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"}}}},"422":{"description":"Invalid url, unknown event, duplicate endpoint, or plan limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/update/{id}":{"put":{"tags":["Webhooks"],"summary":"Update a webhook","description":"Send only the fields you want to change. `active: false` pauses deliveries. An empty `sub_account_ids` array removes the sub-account filter.","parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookInput"},{"type":"object","properties":{"active":{"type":"boolean"}}}]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid url or unknown event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/delete/{id}":{"delete":{"tags":["Webhooks"],"summary":"Delete a webhook","description":"Pending retries for this endpoint are cancelled.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/deliveries/{id}":{"get":{"tags":["Webhooks"],"summary":"Recent deliveries","description":"The last 50 delivery attempts for this webhook, newest first.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Delivery attempts","content":{"application/json":{"schema":{"type":"object","properties":{"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}}}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhooks/test/{id}":{"post":{"tags":["Webhooks"],"summary":"Send a test delivery","description":"Sends a signed sample `review.created` envelope (with `\"test\": true`) to the endpoint and returns what happened.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Delivery result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"status_code":{"type":"integer","example":200},"error":{"type":"string","example":""},"response":{"type":"object"}}}}}},"404":{"description":"Not found or not owned by this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/widgets/types":{"get":{"tags":["Widgets"],"summary":"Widget type catalog","description":"All widget types with their default settings - the reference for which settings keys each type accepts.","responses":{"200":{"description":"Types","content":{"application/json":{"schema":{"type":"object","properties":{"types":{"type":"array","items":{"$ref":"#/components/schemas/WidgetType"}}}}}}}}}},"/widgets/get":{"get":{"tags":["Widgets"],"summary":"List widgets","description":"Widgets of the sub-account the API key is scoped to.","responses":{"200":{"description":"Widgets","content":{"application/json":{"schema":{"type":"object","properties":{"widgets":{"type":"array","items":{"$ref":"#/components/schemas/WidgetSummary"}}}}}}}}}},"/widgets/get/{id}":{"get":{"tags":["Widgets"],"summary":"Get a widget","description":"Full settings plus ready-to-paste embed snippets.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"The widget","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Widget"}}}},"404":{"description":"Not found or not owned by this sub-account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/widgets/{id}/reviews":{"get":{"tags":["Widgets"],"summary":"Get a widget's reviews","description":"The reviews this widget displays - the approved reviews of the sub-account it belongs to. Defaults to approved; accepts the same filters and cursor pagination as GET /reviews/get (rating, min_rating/max_rating, since/until, limit, before_ts/before_id, status).","parameters":[{"$ref":"#/components/parameters/id"},{"name":"rating","in":"query","description":"Star ratings to include, comma separated (1-5). Ratings on other scales are normalized to 5 stars.","schema":{"type":"string","example":"4,5"}},{"name":"channels","in":"query","description":"Channel ids, comma separated. Combine with `sources` to match either.","schema":{"type":"string"}},{"name":"sources","in":"query","description":"Custom source ids, comma separated.","schema":{"type":"string"}},{"name":"tags","in":"query","description":"Tag ids, comma separated.","schema":{"type":"string"}},{"name":"q","in":"query","description":"Searches review text and author names.","schema":{"type":"string"}},{"name":"media_only","in":"query","description":"Only reviews with photos or videos.","schema":{"type":"integer","enum":[1]}},{"name":"with_photos","in":"query","description":"Only reviews with an author photo.","schema":{"type":"integer","enum":[1]}},{"name":"limit","in":"query","description":"Page size, 1-100 (default 25).","schema":{"type":"integer","default":25,"minimum":1,"maximum":100}},{"name":"min_rating","in":"query","description":"Only reviews with a star rating at or above this value (1-5). Ratings on other scales are normalized to 5 stars.","schema":{"type":"number","minimum":1,"maximum":5,"example":4}},{"name":"max_rating","in":"query","description":"Only reviews with a star rating at or below this value (1-5). Ratings on other scales are normalized to 5 stars.","schema":{"type":"number","minimum":1,"maximum":5}},{"name":"since","in":"query","description":"Only reviews posted on or after this date. Accepts a date or datetime, e.g. `2026-01-01` or `2026-01-01 09:00:00`.","schema":{"type":"string","example":"2026-01-01"}},{"name":"until","in":"query","description":"Only reviews posted on or before this date. Accepts a date or datetime, e.g. `2026-06-30` or `2026-06-30 23:59:59`.","schema":{"type":"string"}},{"name":"translate","in":"query","description":"ISO language code, e.g. `es`. Returns each review's `text` translated to this language; the untranslated text is kept in `text_original` and the target code in `text_language`.","schema":{"type":"string","example":"es"}},{"name":"before_ts","in":"query","description":"Pagination cursor - pass the `next_before_ts` of the previous page to fetch the next one. Pass together with `before_id`.","schema":{"type":"integer"}},{"name":"before_id","in":"query","description":"Pagination cursor - pass the `next_before_id` of the previous page alongside `before_ts` to fetch the next one. Both are required together to break ties between reviews with the same timestamp.","schema":{"type":"integer"}}],"responses":{"200":{"description":"A page of the widget's reviews","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewList"}}}},"404":{"description":"Widget not found or not owned by this sub-account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/widgets/add":{"post":{"tags":["Widgets"],"summary":"Create a widget","description":"Creates from the type defaults; optional `settings` overrides are merged in. Requires admin or editor role.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WidgetInput"}}}},"responses":{"201":{"description":"Created, with embed snippets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Widget"}}}},"422":{"description":"Missing or unknown type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/widgets/update/{id}":{"put":{"tags":["Widgets"],"summary":"Update a widget","description":"Send any of settings / active / name. Settings merge recursively - send only the keys to change. Requires admin or editor role.","parameters":[{"$ref":"#/components/parameters/id"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WidgetInput"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Widget"}}}},"404":{"description":"Not found or not owned by this sub-account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/widgets/delete/{id}":{"delete":{"tags":["Widgets"],"summary":"Delete a widget","description":"Requires admin or editor role.","parameters":[{"$ref":"#/components/parameters/id"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Success"}}}},"404":{"description":"Not found or not owned by this sub-account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/discover":{"post":{"tags":["Channels"],"summary":"Discover a business's profiles by name","description":"Find a business's review profiles by name on up to 3 platforms per search. **Google searches use Google Places** (exact, verified, returns place_id); other platforms use web-search discovery. Limited to 30 discoveries per account per day.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","platforms"],"properties":{"name":{"type":"string","example":"Joe's Pizza Brooklyn"},"platforms":{"type":"array","items":{"type":"string"},"description":"Platform types to search - required, 1 to 3 per search (modern aliases accepted). Google searches use Google Places for exact, verified results.","example":["tripadvisor"],"maxItems":3,"minItems":1},"country":{"type":"string","description":"Two-letter country code hint."},"verify":{"type":"boolean","description":"Verify candidates against the live profile (name, rating). Slower; recommended when scoped to one platform."}}}}}},"responses":{"200":{"description":"Candidate profiles","content":{"application/json":{"example":{"candidates":[{"platform":"google","url":"https://www.google.com/maps?q=place_id:ChIJCar0f49ZwokR6ozLV-dHNTE","place_id":"ChIJCar0f49ZwokR6ozLV-dHNTE","verified":true,"business_name":"Katz's Delicatessen","address":"205 E Houston St, New York, NY 10002, USA","score":4.5,"reviews":54507}]}}}},"429":{"description":"Daily discovery limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/rating_history/{id}":{"get":{"tags":["Channels"],"summary":"Rating history for a channel","description":"Daily snapshots of the official score and review count. Dates before monitoring started are reconstructed from the collected reviews (cumulative average) and flagged with `estimated: true`; from the first crawl onward, points are real snapshots of the platform's official figures.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Channel id"},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":90,"maximum":365},"description":"Window size in days (max 365)"}],"responses":{"200":{"description":"History points, oldest first","content":{"application/json":{"schema":{"type":"object","properties":{"history":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","example":"2026-08-01"},"score":{"type":"number","example":4.7},"reviews":{"type":"integer","example":1999},"estimated":{"type":"boolean","description":"Present and true on points reconstructed from collected reviews (pre-monitoring dates)"}}}}}},"example":{"history":[{"date":"2026-06-14","score":4.8,"reviews":112,"estimated":true},{"date":"2026-08-01","score":4.7,"reviews":1999}]}}}},"404":{"description":"Channel not found"}},"security":[{"basicAuth":[]}]}},"/reports/average_rating":{"get":{"tags":["Reports"],"summary":"Average rating over time","description":"Monthly weighted average rating across ALL monitored profiles, derived from the daily rating snapshots. Scores are normalised to the 5-star scale and weighted by each profile's review count; months with no snapshot yet are null.","parameters":[{"name":"months","in":"query","schema":{"type":"integer","default":12,"maximum":24},"description":"Window size in months (max 24)"}],"responses":{"200":{"description":"Month buckets, oldest first","content":{"application/json":{"example":{"months":[{"month":"2026-07","average_rating":4.62},{"month":"2026-08","average_rating":4.64}]}}}}},"security":[{"basicAuth":[]}]}},"/reports/rating_distribution":{"get":{"tags":["Reports"],"summary":"Rating distribution","description":"Star histogram (1-5) across every collected review of the account's monitored profiles.","responses":{"200":{"description":"Counts per star","content":{"application/json":{"example":{"total":12840,"distribution":{"1":420,"2":310,"3":980,"4":3110,"5":8020}}}}}},"security":[{"basicAuth":[]}]}},"/groups":{"get":{"tags":["Properties"],"summary":"List properties","description":"All properties on the sub-account with their profile counts.","responses":{"200":{"description":"Groups","content":{"application/json":{"example":{"groups":[{"id":15,"name":"London locations","profile_count":4}]}}}}},"security":[{"basicAuth":[]}]},"post":{"tags":["Properties"],"summary":"Create a property","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"example":{"success":true,"group":{"id":16,"name":"London locations","profile_count":0}}}}}},"security":[{"basicAuth":[]}]}},"/groups/{id}":{"put":{"tags":["Properties"],"summary":"Rename a property","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated"}},"security":[{"basicAuth":[]}]},"delete":{"tags":["Properties"],"summary":"Delete a property","description":"Deletes the property; profiles in it become ungrouped.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Deleted"}},"security":[{"basicAuth":[]}]}},"/channels/ai/summary/{id}":{"get":{"tags":["Channels"],"summary":"AI review summary","description":"A short AI-generated summary of the profile's reviews. Cheap cached read; it regenerates automatically when enough new reviews arrive (>=5) or the summary is over 30 days old, subject to a per-account daily cap. Returns summary: null until the profile has collected reviews. The language field is \"auto\" when the summary followed the reviews' own language.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"The stored (or freshly generated) summary","content":{"application/json":{"example":{"summary":"Guests praise the fast check-in and spotless rooms; the main gripe is limited weekend parking.","language":"en","based_on_reviews":691,"generated_on":"2026-08-09T09:12:00Z","stale":false}}}}},"security":[{"basicAuth":[]}]},"post":{"tags":["Channels"],"summary":"Regenerate the AI summary","description":"Force a fresh summary. Optional length (target words) and language (default: the reviews' own language). Rate limited: a 60-second per-profile cooldown and a per-account daily generation cap (429 when exceeded). Each call runs an AI generation.","requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"length":{"type":"integer","description":"Target length in words"},"language":{"type":"string","description":"ISO code, e.g. \"en\" - default follows the reviews"}}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Generated","content":{"application/json":{"example":{"success":true,"summary":"...","language":"en","based_on_reviews":691,"generated_on":"2026-08-09T09:12:00Z"}}}},"429":{"description":"Cooldown or daily AI limit reached"}},"security":[{"basicAuth":[]}]}},"/channels/ai/insights/{id}":{"get":{"tags":["Channels"],"summary":"AI insights for a profile","description":"A sentiment breakdown plus the strengths customers praise and the opportunities to improve. Sentiment is derived from the reviews' star ratings; the strengths and improvements come from AI and are cached, regenerating automatically when new reviews arrive. When the profile has fewer than 5 text reviews the response returns `below_threshold: true` with empty `strengths` and `improvements` arrays. Each improvement's `severity` is either \"recurring\" or \"isolated\".","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"language","in":"query","description":"Language for the AI-generated strengths and improvements.","schema":{"type":"string","enum":["en","fr","es","pt","de","it"],"default":"en"}}],"responses":{"200":{"description":"The stored (or freshly generated) insights","content":{"application/json":{"example":{"sentiment":{"positive":982,"neutral":45,"negative":34,"total":1061,"positive_pct":93},"strengths":["Professional and patient instructors","Fun and engaging atmosphere"],"improvements":[{"text":"Consider lowering prices for longer sessions","severity":"recurring"}],"generated_on":"2026-08-10T12:00:00Z","cached":true}}}}},"security":[{"basicAuth":[]}]}}},"components":{"securitySchemes":{"basicAuth":{"type":"http","scheme":"basic","description":"Leave the username empty and use your API key (dashboard → Integrations → API) as the password."}},"parameters":{"id":{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},"settings_id":{"name":"settings_id","in":"query","description":"Invite settings profile id. Omit for the default profile.","schema":{"type":"integer"}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional. Repeating a request with the same key within 24h returns the original response instead of creating a duplicate.","schema":{"type":"string"}}},"responses":{"Unauthorized":{"description":"Missing or invalid API key, or the API integration is disabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationError":{"description":"Validation failed - the response `msg` explains what to fix.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Success":{"type":"object","properties":{"success":{"type":"boolean","example":true}}},"Error":{"type":"object","properties":{"success":{"type":"boolean","example":false},"msg":{"type":"string","example":"invalid date"}}},"Review":{"type":"object","properties":{"id":{"type":"integer","example":88213},"type":{"type":"string","description":"Platform type slug.","example":"google"},"label":{"type":"string","description":"Platform display name.","example":"Google"},"text":{"type":"string","example":"Great service, highly recommended!","description":"Review text as plain text - HTML markup is stripped and line breaks are real newlines. Translated when the `translate` query parameter is used."},"text_original":{"type":"string","description":"Present only when `translate` is used - the untranslated review text.","example":"Buen servicio, muy recomendable!"},"text_language":{"type":"string","description":"Present only when `translate` is used - the ISO language code `text` was translated to.","example":"en"},"sentiment":{"type":"string","nullable":true,"enum":["positive","neutral","negative"],"description":"Sentiment derived from the review's star rating. Null when the review has no rating.","example":"positive"},"from_name":{"type":"string","example":"Jane Cooper"},"from_image":{"type":"string","format":"uri","description":"Author photo."},"from_url":{"type":"string","format":"uri","description":"Author profile on the platform."},"post_url":{"type":"string","format":"uri","description":"The review on the platform."},"rating_value":{"type":"number","example":5},"rating_scale":{"type":"integer","example":5},"posted_on":{"type":"string","example":"2026-08-01 14:22:10"},"tags":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}}}},"additional":{"type":"object","nullable":true,"description":"Additional review inputs collected with the review (custom fields)."},"media":{"type":"array","description":"All attached media.","items":{"type":"object","properties":{"type":{"type":"string","enum":["image","video","audio"]},"url":{"type":"string","format":"uri"},"src":{"type":"string","format":"uri","description":"Playable file for videos, when available."}}}}}},"ReviewList":{"type":"object","properties":{"count":{"type":"integer","description":"Total reviews matching the filters.","example":128},"limit":{"type":"integer","example":10},"items":{"type":"array","items":{"$ref":"#/components/schemas/Review"}},"next_before_ts":{"type":"integer","description":"Pass as `before_ts` (together with `next_before_id`) to fetch the next page. Absent when there are no items.","example":1785932530},"next_before_id":{"type":"integer","description":"Pass as `before_id` (together with `next_before_ts`) to fetch the next page. Absent when there are no items.","example":88213}}},"ReviewCreate":{"type":"object","required":["from_name","text","posted_on"],"properties":{"from_name":{"type":"string","example":"Jane Cooper"},"text":{"type":"string","example":"Great service, highly recommended!","description":"Review text as plain text - HTML markup is stripped and line breaks are real newlines."},"posted_on":{"type":"string","description":"ISO date or common `d/m/Y`-style formats.","example":"2026-08-01"},"rating_value":{"type":"integer","minimum":0,"maximum":5,"example":5},"source":{"type":"string","description":"Name of one of your custom sources."},"from_image":{"type":"string","format":"uri"},"post_url":{"type":"string","format":"uri"},"photo":{"type":"string","description":"Image URLs, comma separated."},"video":{"type":"string","description":"YouTube / Vimeo / direct video URLs, comma separated."}}},"BulkResult":{"type":"object","properties":{"success":{"type":"boolean"},"validation":{"type":"boolean","description":"Whether this was a validate-only run."},"valid":{"type":"integer"},"invalid":{"type":"integer"},"errors":{"type":"array","items":{"type":"object"}},"data":{"type":"array","items":{"type":"object"}}}},"Channel":{"type":"object","properties":{"id":{"type":"integer","example":5121},"type":{"type":"string","description":"Platform type slug.","example":"google"},"label":{"type":"string","example":"Google"},"logo":{"type":"string","format":"uri","description":"Platform logo, 60x60 square."},"name":{"type":"string","description":"Connected profile / business name.","example":"Acme Coffee Roasters"},"image_url":{"type":"string","format":"uri","description":"The business's own image on the platform (avatar/photo), when available - often empty for competitor profiles."},"url":{"type":"string","format":"uri","description":"The connected profile URL."},"disabled":{"type":"integer"},"errors":{"type":"integer","description":"Consecutive crawl errors - non-zero usually means the profile URL changed."},"official_score":{"type":"number","description":"The platform's official rating for the profile.","example":4.7},"official_num_reviews":{"type":"integer","example":234},"competitor":{"type":"integer","description":"1 when marked as a competitor profile.","example":0},"group":{"type":"object","description":"Group the profile belongs to, or null.","properties":{"id":{"type":"integer"},"name":{"type":"string"}},"nullable":true},"labels":{"type":"array","items":{"type":"string"},"description":"Custom labels."},"checked_minutes_ago":{"type":"integer","description":"Minutes since the last crawl, measured on the server clock.","example":42},"logo_big":{"type":"string","description":"Platform logo, large."},"logo_icon":{"type":"string","description":"Platform logo, 20x20 (small icon)."}}},"ChannelType":{"type":"object","properties":{"type":{"type":"string","example":"tripadvisor"},"label":{"type":"string","example":"TripAdvisor"},"logo":{"type":"string","format":"uri"},"groups":{"type":"array","items":{"type":"string"},"example":["Hospitality"]},"connect":{"type":"object","properties":{"method":{"type":"string","enum":["url","credentials","oauth"],"description":"`url` - connect with POST /channels/add and a profile URL; `credentials` - connect with POST /channels/add and the platform's API key (Delighted); `oauth` - use the authorize_url flow."},"params":{"type":"array","items":{"type":"string"},"description":"Parameters this platform accepts."},"instructions":{"type":"string","description":"Human-readable connection instructions for this platform."},"url_example":{"type":"string","description":"Example profile URL format, when applicable."}}},"metadata":{"type":"object","description":"Platform-specific options (selectable stores / countries), present on the single-type endpoint for platforms that have them."}}},"InviteRequest":{"type":"object","properties":{"id":{"type":"integer"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"type":{"type":"string","enum":["email","sms"]},"name":{"type":"string","example":"Jane Cooper"},"recipient":{"type":"string","example":"customer@example.com"},"schedule":{"type":"string","example":"2026-08-10 10:00:00"},"sent_at":{"type":"string","nullable":true},"status":{"type":"string","example":"pending"},"data":{"type":"object","nullable":true},"parent_id":{"type":"integer","description":"0 for a first email; the root request id for follow-up steps.","example":0},"step":{"type":"integer","description":"Step number within the follow-up sequence (2, 3, …).","nullable":true},"template_id":{"type":"integer","description":"Template used for this step, 0 = the settings' default."}}},"InviteRequestCreate":{"type":"object","required":["recipient"],"properties":{"recipient":{"type":"string","description":"Email address or phone number.","example":"customer@example.com"},"name":{"type":"string","example":"Jane Cooper"},"delay_unit":{"type":"string","enum":["minutes","hours","days"],"description":"Override the settings' default delay."},"delay_amount":{"type":"integer","example":3},"settings_id":{"type":"integer","description":"Use a specific settings profile."}}},"InviteSettings":{"type":"object","description":"Only the fields you send are updated.","properties":{"name":{"type":"string","example":"default"},"delay":{"type":"object","properties":{"unit":{"type":"string","enum":["minutes","hours","days"]},"amount":{"type":"integer","example":3}}},"sequence":{"type":"object","description":"Automatic follow-up reminders. A click, unsubscribe, failure or review on any step cancels the remaining steps.","properties":{"enabled":{"type":"boolean"},"steps":{"type":"array","maxItems":3,"items":{"type":"object","properties":{"delay_days":{"type":"integer","minimum":1,"maximum":60,"description":"Relative to the first email's send time."},"template_id":{"type":"integer","description":"0 = the settings' default template."}}},"example":[{"delay_days":3,"template_id":0},{"delay_days":7,"template_id":0}]}}},"cooldown_days":{"type":"integer","description":"Don't re-invite a recipient who already got a request within N days. 0 = off."},"send_window":{"type":"object","description":"Only send within this hour window.","properties":{"enabled":{"type":"boolean"},"from":{"type":"integer","minimum":0,"maximum":23},"to":{"type":"integer","minimum":0,"maximum":23},"utc_offset":{"type":"integer"}}},"templates":{"type":"object","description":"Email and SMS template configuration."}}},"CollectStats":{"type":"object","properties":{"stats":{"type":"object","properties":{"views":{"type":"integer"},"form_submits":{"type":"integer"},"wf_copies":{"type":"integer"},"wf_done":{"type":"integer"},"wf_gated":{"type":"integer"},"wf_reminded":{"type":"integer"},"source_clicks_total":{"type":"integer"},"source_clicks":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"count":{"type":"integer"}}}},"flows":{"type":"object"},"daily":{"type":"array","items":{"type":"object"}},"ab":{"type":"object","description":"Write-first vs choose-first comparison."}}},"days":{"type":"integer"}}},"Webhook":{"type":"object","properties":{"id":{"type":"integer","example":42},"url":{"type":"string","example":"https://example.com/hooks/repuso"},"events":{"type":"array","items":{"type":"string","enum":["review.created","rating.changed","profile.error"]},"example":["review.created","rating.changed"]},"format":{"type":"string","description":"`api` for webhooks created via this API (signed, versioned envelope). `legacy` for webhooks created in the dashboard Integrations screen.","example":"api"},"active":{"type":"boolean","example":true},"error":{"type":"boolean","description":"True when the endpoint kept failing and deliveries were suspended.","example":false},"sub_account_ids":{"type":"array","items":{"type":"integer"},"description":"Only present when the webhook is filtered to specific sub-accounts. `0` is the main account."},"profile_ids":{"type":"array","items":{"type":"integer"},"description":"Present when the webhook is scoped to specific profiles."},"group_ids":{"type":"array","items":{"type":"integer"},"description":"Present when the webhook is scoped to groups."}}},"WebhookWithSecret":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"secret":{"type":"string","description":"Signing secret. **Returned only once, on creation** - store it on your side.","example":"whsec_cfce0521a2b472d9fa5d39701e79d6b7b23c6510a6e921aa"}}}]},"WebhookInput":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"HTTPS endpoint to deliver events to.","example":"https://example.com/hooks/repuso"},"events":{"type":"array","items":{"type":"string","enum":["review.created","rating.changed","profile.error"]},"description":"Events to subscribe to. Omit to subscribe to all events."},"sub_account_ids":{"type":"array","items":{"type":"integer"},"description":"Restrict deliveries to these sub-accounts (`0` = main account). Omit or send an empty array for no filter."},"profile_ids":{"type":"array","items":{"type":"integer"},"description":"Deliver only events of these profiles. Empty array removes the filter."},"group_ids":{"type":"array","items":{"type":"integer"},"description":"Deliver only events of profiles in these groups. Empty array removes the filter."}}},"WebhookDelivery":{"type":"object","properties":{"event":{"type":"string","example":"review.created"},"executed_on":{"type":"string","example":"2026-08-06 14:37:35"},"status_code":{"type":"integer","example":200},"success":{"type":"boolean","example":true},"retry_cycle":{"type":"integer","description":"1 = 5-minute retries, 2 = hourly, 3 = daily.","example":1}}},"WebhookEnvelope":{"type":"object","description":"The JSON body POSTed to your endpoint.","properties":{"id":{"type":"string","example":"evt_293060"},"event":{"type":"string","example":"review.created"},"occurred_on":{"type":"string","example":"2026-08-06T14:37:32Z"},"data":{"type":"object","description":"Event payload. For review.*: the review (id, type, date, text, from_name, from_image, from_url, rating_value, rating_scale, additional) plus profile_id, profile_name and platform. For rating.changed: profile_id, platform, profile_name, old_score, new_score, old_num_reviews, new_num_reviews. For profile.error: profile_id, platform, profile_name, error, disabled."}}},"WidgetType":{"type":"object","properties":{"type":{"type":"integer","description":"Numeric type id - use this in POST /widgets/add.","example":2},"type_slug":{"type":"string","example":"grid"},"label":{"type":"string","example":"Grid"},"embed_kind":{"type":"string","enum":["script","image","signature"],"description":"script = JS embed, image = static image badge, signature = email signature HTML."},"default_settings":{"type":"object","description":"The full settings object for this type with default values. These are the keys you can override in settings."}}},"WidgetSummary":{"type":"object","properties":{"id":{"type":"integer","example":1449},"type_slug":{"type":"string","example":"grid"},"label":{"type":"string","example":"Grid"},"name":{"type":"string","example":"Homepage grid"},"active":{"type":"boolean","example":true}}},"Widget":{"type":"object","properties":{"id":{"type":"integer","example":1449},"type":{"type":"integer","example":2},"type_slug":{"type":"string","example":"grid"},"label":{"type":"string","example":"Grid"},"name":{"type":"string","example":"Homepage grid"},"active":{"type":"boolean","example":true},"settings":{"type":"object","description":"Full effective settings (type defaults merged with overrides). Keys vary per type - see GET /widgets/types."},"embed":{"type":"object","properties":{"html":{"type":"string","description":"Paste-anywhere embed snippet."},"advanced":{"type":"object","description":"Separate head script + custom element tag, for stricter CSPs."},"wordpress_shortcode":{"type":"string","example":"[rw_grid id=\"1449\"]"}}}}},"WidgetInput":{"type":"object","properties":{"type":{"type":"integer","description":"Required on create. Numeric type id from GET /widgets/types.","example":2},"name":{"type":"string","description":"Internal label for the widget.","example":"Homepage grid"},"settings":{"type":"object","description":"Any subset of the type's settings keys; merged recursively over defaults."},"active":{"type":"boolean","description":"Update only. false pauses rendering."}}}}}}