openapi: 3.0.3
info:
title: 'Subsig API Documentation'
description: ''
version: 1.0.0
servers:
-
url: 'https://backend-staging.subsig.com'
tags:
-
name: Registration
description: 'Create a new user account to access the application.'
-
name: Authentication
description: "\nAPIs for user authentication"
-
name: 'Password Reset'
description: 'Recover access to your account if you forgot your password.'
-
name: Reviews
description: "\nAPIs for fetching aggregated reviews from multiple platforms, scoped to organizations."
-
name: 'Social Posts'
description: "\nAPIs for fetching and filtering social media mentions from multiple platforms, scoped to organizations."
-
name: Endpoints
description: ''
-
name: Invites
description: "\nAPIs for managing invites"
-
name: Links
description: "\nAPIs for managing project links"
-
name: Notifications
description: "\nAPIs for managing notification rules"
-
name: Onboarding
description: "\nAPIs for user onboarding flow"
-
name: Organisations
description: "\nAPIs for managing organisations"
-
name: Platforms
description: "\nAPIs for managing platforms"
-
name: Profiles
description: "\nAPIs for managing product claims and product profile data.\n\nThe Profiles API group covers two related workflows:\n\n1. **Claim Profiles** - Submit and manage ownership claims for products\n2. **Product Profiles** - Edit and sync detailed product information"
-
name: Projects
description: "\nAPIs for managing projects within organisations"
-
name: 'Review Views'
description: "\nAPIs for managing saved review filter views (organization-scoped)."
-
name: 'Slack Integration'
description: "\nAPIs for connecting and managing Slack workspace integration"
-
name: Subscriptions
description: "\nAPIs for managing subscriptions"
components:
securitySchemes:
default:
type: http
scheme: bearer
description: 'You can retrieve your token by visiting your dashboard and clicking Generate API token.'
security:
-
default: []
paths:
/register:
post:
summary: 'Create Account'
operationId: createAccount
description: 'Register a new user account. After successful registration, the user will be automatically logged in and redirected to the dashboard.'
parameters: []
responses:
201:
description: 'Account created. User logged in and redirected.'
content:
text/plain:
schema:
type: string
example: ''
422:
description: 'Validation error.'
content:
application/json:
schema:
type: object
example:
message: 'The email has already been taken.'
errors:
email:
- 'The email has already been taken.'
properties:
message:
type: string
example: 'The email has already been taken.'
errors:
type: object
properties:
email:
type: array
example:
- 'The email has already been taken.'
items:
type: string
tags:
- Registration
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'Full name of the user.'
example: 'John Doe'
email:
type: string
description: 'Valid email address. Must be unique.'
example: john@example.com
password:
type: string
description: 'Password (min 8 characters, at least one uppercase letter, one lowercase letter, one number and one special character).'
example: SecurePass123!
password_confirmation:
type: string
description: 'Must match password exactly.'
example: SecurePass123!
required:
- name
- email
- password
- password_confirmation
security: []
/api/sanctum/token:
post:
summary: 'Create API Token'
operationId: createAPIToken
description: "Generate an API token for authenticated requests.\nRequires a verified email address."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
token: 1|abc123...
properties:
token:
type: string
example: 1|abc123...
422:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Invalid credentials'
type: object
example:
message: 'The provided credentials are incorrect.'
errors:
email:
- 'The provided credentials are incorrect.'
properties:
message:
type: string
example: 'The provided credentials are incorrect.'
errors:
type: object
properties:
email:
type: array
example:
- 'The provided credentials are incorrect.'
items:
type: string
-
description: 'Email not verified'
type: object
example:
message: 'Please verify your email address before logging in.'
errors:
email:
- 'Please verify your email address before logging in.'
properties:
message:
type: string
example: 'Please verify your email address before logging in.'
errors:
type: object
properties:
email:
type: array
example:
- 'Please verify your email address before logging in.'
items:
type: string
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: "The user's email address."
example: john@example.com
password:
type: string
description: "The user's password."
example: SecurePass123!
required:
- email
- password
security: []
/api/email/verify:
post:
summary: 'Verify Email'
operationId: verifyEmail
description: "Verify user's email address using the 4-digit code sent via email.\nReturns an API token on successful verification.\nVerification link is sent via email. /verify-email?code=1234&email=john@example.com"
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: ''
type: object
example:
message: 'Email verified successfully.'
token: 1|abc123...
properties:
message:
type: string
example: 'Email verified successfully.'
token:
type: string
example: 1|abc123...
-
description: 'Already verified'
type: object
example:
message: 'Email already verified.'
token: 1|abc123...
properties:
message:
type: string
example: 'Email already verified.'
token:
type: string
example: 1|abc123...
422:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Invalid code'
type: object
example:
message: 'Invalid verification code.'
errors:
code:
- 'Invalid verification code.'
properties:
message:
type: string
example: 'Invalid verification code.'
errors:
type: object
properties:
code:
type: array
example:
- 'Invalid verification code.'
items:
type: string
-
description: 'Expired code'
type: object
example:
message: 'Verification code has expired. Please request a new one.'
errors:
code:
- 'Verification code has expired. Please request a new one.'
properties:
message:
type: string
example: 'Verification code has expired. Please request a new one.'
errors:
type: object
properties:
code:
type: array
example:
- 'Verification code has expired. Please request a new one.'
items:
type: string
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: "The user's email address."
example: john@example.com
code:
type: string
description: 'The 4-digit verification code.'
example: '1234'
required:
- email
- code
security: []
/api/email/resend:
post:
summary: 'Resend Verification Code'
operationId: resendVerificationCode
description: "Send a new 4-digit verification code to the user's email.\nCode expires in 60 minutes."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: ''
type: object
example:
message: 'Verification code sent.'
properties:
message:
type: string
example: 'Verification code sent.'
-
description: 'Already verified'
type: object
example:
message: 'Email already verified.'
properties:
message:
type: string
example: 'Email already verified.'
422:
description: 'User not found'
content:
application/json:
schema:
type: object
example:
message: 'No account found with this email.'
errors:
email:
- 'No account found with this email.'
properties:
message:
type: string
example: 'No account found with this email.'
errors:
type: object
properties:
email:
type: array
example:
- 'No account found with this email.'
items:
type: string
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: "The user's email address."
example: john@example.com
required:
- email
security: []
/api/invites/accept:
post:
summary: 'Register with Invite'
operationId: registerWithInvite
description: "Accept an invitation and create a new user account. The email address must match\nthe email address on the invite. After successful registration, the user will be\nadded to the organisation or project and will receive an email verification code.\n\nNote: This endpoint bypasses the business email requirement since the invitation\nitself validates the user's legitimacy."
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Registration successful. Please check your email to verify your account.'
properties:
message:
type: string
example: 'Registration successful. Please check your email to verify your account.'
404:
description: 'Token not found'
content:
application/json:
schema:
type: object
example:
message: 'Invite not found.'
errors:
token:
- 'The invite token is invalid or does not exist.'
properties:
message:
type: string
example: 'Invite not found.'
errors:
type: object
properties:
token:
type: array
example:
- 'The invite token is invalid or does not exist.'
items:
type: string
410:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Invite expired'
type: object
example:
message: 'Invite has expired.'
errors:
token:
- 'This invite has expired. Please request a new invitation.'
properties:
message:
type: string
example: 'Invite has expired.'
errors:
type: object
properties:
token:
type: array
example:
- 'This invite has expired. Please request a new invitation.'
items:
type: string
-
description: 'Invite already accepted'
type: object
example:
message: 'Invite has already been accepted.'
errors:
token:
- 'This invite has already been accepted.'
properties:
message:
type: string
example: 'Invite has already been accepted.'
errors:
type: object
properties:
token:
type: array
example:
- 'This invite has already been accepted.'
items:
type: string
422:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Email mismatch'
type: object
example:
message: 'The email address does not match the invitation.'
errors:
email:
- 'The email address must match the email on the invitation.'
properties:
message:
type: string
example: 'The email address does not match the invitation.'
errors:
type: object
properties:
email:
type: array
example:
- 'The email address must match the email on the invitation.'
items:
type: string
-
description: 'Validation error'
type: object
example:
message: 'The name field is required.'
errors:
name:
- 'The name field is required.'
properties:
message:
type: string
example: 'The name field is required.'
errors:
type: object
properties:
name:
type: array
example:
- 'The name field is required.'
items:
type: string
-
description: 'Email already registered'
type: object
example:
message: 'The email has already been taken.'
errors:
email:
- 'The email has already been taken.'
properties:
message:
type: string
example: 'The email has already been taken.'
errors:
type: object
properties:
email:
type: array
example:
- 'The email has already been taken.'
items:
type: string
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
token:
type: string
description: 'The invite token from the invitation link.'
example: eEtgjrcdtubjCu4817MfGiimvC2DQLBgaI7LpY1g5kdDMK5wJlQank7ZJ6PWurmb
name:
type: string
description: "The user's full name."
example: 'John Doe'
email:
type: string
description: "The user's email address (must match the invite email)."
example: user@example.com
password:
type: string
description: "The user's password (min 8 characters)."
example: SecurePass123!
password_confirmation:
type: string
description: 'Password confirmation.'
example: SecurePass123!
required:
- token
- name
- email
- password
- password_confirmation
security: []
/api/user:
get:
summary: 'Get Current User'
operationId: getCurrentUser
description: "Get the authenticated user's details including organisation and subscription information."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
name: 'John Doe'
email: john@example.com
email_verified_at: '2025-12-04T12:00:00.000000Z'
created_at: '2025-12-04T10:00:00.000000Z'
organisation:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Inc'
website: 'https://acme.com'
product_logo: 'https://example.com/logo.png'
role: organisation_owner
subscription:
id: 1
stripe_price_id: price_1234567890
name: 'Pro Plan'
status: active
expiration_date: '2025-12-31T23:59:59.000000Z'
trial_end_date: '2025-12-11T23:59:59.000000Z'
properties:
name:
type: string
example: 'John Doe'
email:
type: string
example: john@example.com
email_verified_at:
type: string
example: '2025-12-04T12:00:00.000000Z'
created_at:
type: string
example: '2025-12-04T10:00:00.000000Z'
organisation:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Inc'
website:
type: string
example: 'https://acme.com'
product_logo:
type: string
example: 'https://example.com/logo.png'
role:
type: string
example: organisation_owner
subscription:
type: object
properties:
id:
type: integer
example: 1
stripe_price_id:
type: string
example: price_1234567890
name:
type: string
example: 'Pro Plan'
status:
type: string
example: active
expiration_date:
type: string
example: '2025-12-31T23:59:59.000000Z'
trial_end_date:
type: string
example: '2025-12-11T23:59:59.000000Z'
401:
description: Unauthenticated
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Authentication
/login:
post:
summary: 'Log In'
operationId: logIn
description: 'Authenticate with your email and password to start a session. On success, you receive a token for subsequent requests.'
parameters: []
responses:
200:
description: 'Login successful. Session started.'
content:
text/plain:
schema:
type: string
example: ''
422:
description: 'Invalid credentials.'
content:
application/json:
schema:
type: object
example:
message: 'These credentials do not match our records.'
errors:
email:
- 'These credentials do not match our records.'
properties:
message:
type: string
example: 'These credentials do not match our records.'
errors:
type: object
properties:
email:
type: array
example:
- 'These credentials do not match our records.'
items:
type: string
tags:
- Authentication
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: 'Your registered email address.'
example: john@example.com
password:
type: string
description: 'Your account password.'
example: SecurePass123!
remember:
type: boolean
description: 'Stay logged in for extended period.'
example: true
required:
- email
- password
security: []
/logout:
post:
summary: 'Log Out'
operationId: logOut
description: 'End your current session. You will need to log in again to access protected resources.'
parameters: []
responses:
200:
description: 'Logged out successfully.'
content:
text/plain:
schema:
type: string
example: ''
401:
description: 'Not logged in.'
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
tags:
- Authentication
/forgot-password:
post:
summary: 'Request Password Reset'
operationId: requestPasswordReset
description: 'Send a password reset link to your email. The link expires after 60 minutes. Same response for security even if email not found.'
parameters: []
responses:
200:
description: 'Reset link sent.'
content:
application/json:
schema:
type: object
example:
status: 'We have emailed your password reset link.'
properties:
status:
type: string
example: 'We have emailed your password reset link.'
tags:
- 'Password Reset'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: 'Email address associated with your account.'
example: john@example.com
required:
- email
security: []
/reset-password:
post:
summary: 'Reset Password'
operationId: resetPassword
description: 'Set a new password using the token from your email. Token is valid for 60 minutes.'
parameters: []
responses:
200:
description: 'Password reset successful.'
content:
application/json:
schema:
type: object
example:
status: 'Your password has been reset.'
properties:
status:
type: string
example: 'Your password has been reset.'
422:
description: 'Invalid or expired token.'
content:
application/json:
schema:
type: object
example:
message: 'This password reset token is invalid.'
errors:
email:
- 'This password reset token is invalid.'
properties:
message:
type: string
example: 'This password reset token is invalid.'
errors:
type: object
properties:
email:
type: array
example:
- 'This password reset token is invalid.'
items:
type: string
tags:
- 'Password Reset'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
token:
type: string
description: 'Reset token from the email link.'
example: a1b2c3d4e5f6g7h8i9j0
email:
type: string
description: 'Your account email address.'
example: john@example.com
password:
type: string
description: 'Password (min 8 characters, at least one letter and one number).'
example: NewSecurePass123!
password_confirmation:
type: string
description: 'Must match new password exactly.'
example: NewSecurePass123!
required:
- token
- email
- password
- password_confirmation
security: []
'/api/organisations/{organisation_uuid}/reviews':
post:
summary: 'Get Organization Reviews'
operationId: getOrganizationReviews
description: "Fetch and aggregate reviews from multiple projects/brands within an organization.\nReviews are fetched from external API, persisted to database, and returned with filters applied.\nSupports filtering by multiple brands (projects), platforms, ratings, languages, and more."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
organisation_id: 660e8400-e29b-41d4-a716-446655440001
organisation_name: 'Acme Corp'
reviews:
current_page: 1
data:
-
id: 1
scraper_review_id: 10452
organisation_id: 5
project_id: 30
platform: Capterra
platform_icon: null
rating: 5.0
content: 'Great product with excellent features...'
author: 'John Smith'
job_role: 'Product Manager'
language: en
date: '2026-01-13'
link_url: 'https://www.capterra.com/reviews/'
direct_review_url: 'https://scraper.example.com/reviews'
created_at: '2026-01-15T09:30:21.000000Z'
per_page: 10
total: 150
properties:
data:
type: object
properties:
organisation_id:
type: string
example: 660e8400-e29b-41d4-a716-446655440001
organisation_name:
type: string
example: 'Acme Corp'
reviews:
type: object
properties:
current_page:
type: integer
example: 1
data:
type: array
example:
-
id: 1
scraper_review_id: 10452
organisation_id: 5
project_id: 30
platform: Capterra
platform_icon: null
rating: 5
content: 'Great product with excellent features...'
author: 'John Smith'
job_role: 'Product Manager'
language: en
date: '2026-01-13'
link_url: 'https://www.capterra.com/reviews/'
direct_review_url: 'https://scraper.example.com/reviews'
created_at: '2026-01-15T09:30:21.000000Z'
items:
type: object
properties:
id:
type: integer
example: 1
scraper_review_id:
type: integer
example: 10452
organisation_id:
type: integer
example: 5
project_id:
type: integer
example: 30
platform:
type: string
example: Capterra
platform_icon:
type: string
example: null
rating:
type: number
example: 5.0
content:
type: string
example: 'Great product with excellent features...'
author:
type: string
example: 'John Smith'
job_role:
type: string
example: 'Product Manager'
language:
type: string
example: en
date:
type: string
example: '2026-01-13'
link_url:
type: string
example: 'https://www.capterra.com/reviews/'
direct_review_url:
type: string
example: 'https://scraper.example.com/reviews'
created_at:
type: string
example: '2026-01-15T09:30:21.000000Z'
per_page:
type: integer
example: 10
total:
type: integer
example: 150
403:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'You do not have access to this organisation.'
properties:
message:
type: string
example: 'You do not have access to this organisation.'
404:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Organisation not found.'
properties:
message:
type: string
example: 'Organisation not found.'
tags:
- Reviews
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
brands:
type: array
description: 'Optional - Filter by brand/project UUIDs. Empty/omitted = all brands.'
example:
- uuid-1
- uuid-2
items:
type: string
view_uuid:
type: string
description: 'Optional - Apply filters from a saved view. When provided, all other filters (except search and page) are ignored.'
example: a1b2c3d4-e5f6-g7h8-i9j0
nullable: true
platforms:
type: array
description: 'Optional - Filter by platforms. Ignored if view_uuid is provided.'
example:
- G2
- Capterra
items:
type: string
date_range:
type: object
description: ''
example: null
properties:
type:
type: string
description: 'This field is required when date_range is present.'
example: all_time
enum:
- preset
- custom
- all_time
value:
type: string
description: 'This field is required when date_range.type is preset.'
example: all_time
enum:
- last_14_days
- last_3_months
- last_12_months
- last_3_years
- all_time
nullable: true
date_range_custom:
type: object
description: ''
example: null
properties:
start:
type: string
description: 'This field is required when date_range_custom is present. Must be a valid date.'
example: '2026-02-03T13:58:49'
end:
type: string
description: 'This field is required when date_range_custom is present. Must be a valid date. Must be a date after or equal to date_range_custom.start.'
example: '2052-02-27'
nullable: true
date_from:
type: string
description: 'Optional - Start date. Ignored if view_uuid is provided.'
example: '2026-01-01'
nullable: true
date_to:
type: string
description: 'Optional - End date. Ignored if view_uuid is provided.'
example: '2026-01-24'
nullable: true
rating_buckets:
type: array
description: 'Optional - Filter by rating buckets. Ignored if view_uuid is provided.'
example:
- '5.0'
- 4.0-4.9
items:
type: string
languages:
type: array
description: 'Optional - Filter by languages (ISO 639-1 codes). Ignored if view_uuid is provided.'
example:
- en
- de
items:
type: string
read_status:
type: string
description: 'Optional - Filter by read status. Ignored if view_uuid is provided.'
example: unread
nullable: true
search:
type: string
description: 'Optional - Keyword search. Can be used with view_uuid to search within saved view filters.'
example: 'customer support'
nullable: true
sort_by:
type: string
description: ''
example: review_date
enum:
- review_date
- rating
- platform
- created_at
nullable: true
sort_direction:
type: string
description: ''
example: desc
enum:
- asc
- desc
nullable: true
period_in_days:
type: integer
description: 'Optional - Number of days to fetch reviews for. Default: 30. Ignored if view_uuid is provided.'
example: 30
nullable: true
page:
type: integer
description: 'Optional - Page number for pagination. Default: 1.'
example: 1
nullable: true
parameters:
-
in: path
name: organisation_uuid
description: 'The organization UUID.'
example: 660e8400-e29b-41d4-a716-446655440001
required: true
schema:
type: string
'/api/organisations/{organisation_uuid}/reviews/analytics':
post:
summary: 'Get Review Analytics'
operationId: getReviewAnalytics
description: "Calculate analytics metrics for reviews including new reviews count, average rating,\nreviews per month, with comparison to previous period. Also includes rating distribution\nand platform breakdown."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
product_uuid: 660e8400-e29b-41d4-a716-446655440001
product_name: 'Acme Product'
product_logo: 'https://...'
period:
start: '2025-10-28'
end: '2026-01-27'
duration: 3
metrics:
new_reviews:
current: 2
previous: 10
change_percentage: -80.0
trend: down
average_rating:
current: 4.1
previous: 4.4
change_percentage: -6.8
trend: down
reviews_per_month:
current: 2
previous: 6
change_percentage: -68.3
trend: down
review_velocity:
interval: week
data:
-
group: 2025-10-W5
count: 1
cumulative_before: 0
rating: 4.2
breakdown:
ratings:
'5.0': 0
'4.0': 2
'3.0': 0
'2.0': 0
'1.0': 0
platforms:
G2: 1
Capterra: 1
competitors:
-
project_uuid: 660e8400-e29b-41d4-a716-446655440001
product_name: 'Base Product'
product_logo: 'https://...'
is_competitor: false
total_reviews:
current: 10
previous: 5
change_percentage: 100.0
trend: up
average_rating:
current: 4.3
previous: 4.1
change_percentage: 4.9
trend: up
rank: 1
applied_filters:
brands:
- uuid-1
platforms:
- G2
- Capterra
date_range:
type: preset
value: last_3_months
organisation_id: 660e8400-e29b-41d4-a716-446655440001
organisation_name: 'Acme Corp'
properties:
data:
type: array
example:
-
product_uuid: 660e8400-e29b-41d4-a716-446655440001
product_name: 'Acme Product'
product_logo: 'https://...'
period:
start: '2025-10-28'
end: '2026-01-27'
duration: 3
metrics:
new_reviews:
current: 2
previous: 10
change_percentage: -80
trend: down
average_rating:
current: 4.1
previous: 4.4
change_percentage: -6.8
trend: down
reviews_per_month:
current: 2
previous: 6
change_percentage: -68.3
trend: down
review_velocity:
interval: week
data:
-
group: 2025-10-W5
count: 1
cumulative_before: 0
rating: 4.2
breakdown:
ratings:
'5.0': 0
'4.0': 2
'3.0': 0
'2.0': 0
'1.0': 0
platforms:
G2: 1
Capterra: 1
items:
type: object
properties:
product_uuid:
type: string
example: 660e8400-e29b-41d4-a716-446655440001
product_name:
type: string
example: 'Acme Product'
product_logo:
type: string
example: 'https://...'
period:
type: object
properties:
start:
type: string
example: '2025-10-28'
end:
type: string
example: '2026-01-27'
duration:
type: integer
example: 3
metrics:
type: object
properties:
new_reviews:
type: object
properties:
current:
type: integer
example: 2
previous:
type: integer
example: 10
change_percentage:
type: number
example: -80.0
trend:
type: string
example: down
average_rating:
type: object
properties:
current:
type: number
example: 4.1
previous:
type: number
example: 4.4
change_percentage:
type: number
example: -6.8
trend:
type: string
example: down
reviews_per_month:
type: object
properties:
current:
type: integer
example: 2
previous:
type: integer
example: 6
change_percentage:
type: number
example: -68.3
trend:
type: string
example: down
review_velocity:
type: object
properties:
interval:
type: string
example: week
data:
type: array
example:
-
group: 2025-10-W5
count: 1
cumulative_before: 0
rating: 4.2
items:
type: object
properties:
group: { type: string, example: 2025-10-W5 }
count: { type: integer, example: 1 }
cumulative_before: { type: integer, example: 0 }
rating: { type: number, example: 4.2 }
breakdown:
type: object
properties:
ratings:
type: object
properties:
'5.0':
type: integer
example: 0
'4.0':
type: integer
example: 2
'3.0':
type: integer
example: 0
'2.0':
type: integer
example: 0
'1.0':
type: integer
example: 0
platforms:
type: object
properties:
G2:
type: integer
example: 1
Capterra:
type: integer
example: 1
competitors:
type: array
example:
-
project_uuid: 660e8400-e29b-41d4-a716-446655440001
product_name: 'Base Product'
product_logo: 'https://...'
is_competitor: false
total_reviews:
current: 10
previous: 5
change_percentage: 100
trend: up
average_rating:
current: 4.3
previous: 4.1
change_percentage: 4.9
trend: up
rank: 1
items:
type: object
properties:
project_uuid:
type: string
example: 660e8400-e29b-41d4-a716-446655440001
product_name:
type: string
example: 'Base Product'
product_logo:
type: string
example: 'https://...'
is_competitor:
type: boolean
example: false
total_reviews:
type: object
properties:
current:
type: integer
example: 10
previous:
type: integer
example: 5
change_percentage:
type: number
example: 100.0
trend:
type: string
example: up
average_rating:
type: object
properties:
current:
type: number
example: 4.3
previous:
type: number
example: 4.1
change_percentage:
type: number
example: 4.9
trend:
type: string
example: up
rank:
type: integer
example: 1
applied_filters:
type: object
properties:
brands:
type: array
example:
- uuid-1
items:
type: string
platforms:
type: array
example:
- G2
- Capterra
items:
type: string
date_range:
type: object
properties:
type:
type: string
example: preset
value:
type: string
example: last_3_months
organisation_id:
type: string
example: 660e8400-e29b-41d4-a716-446655440001
organisation_name:
type: string
example: 'Acme Corp'
403:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'You do not have access to this organisation.'
properties:
message:
type: string
example: 'You do not have access to this organisation.'
404:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Organisation not found.'
properties:
message:
type: string
example: 'Organisation not found.'
tags:
- Reviews
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
brands:
type: array
description: 'Optional - Filter by brand/project UUIDs. Empty/omitted = all brands. Ignored if compare_products is provided.'
example:
- uuid-1
- uuid-2
items:
type: string
compare_products:
type: array
description: 'Optional - Compare up to 2 products. When provided, returns array of analytics per product. Takes precedence over brands.'
example:
- uuid-1
- uuid-2
items:
type: string
platforms:
type: array
description: 'Optional - Filter by platforms.'
example:
- G2
- Capterra
items:
type: string
date_range:
type: object
description: 'Optional - Preset date range.'
example:
type: preset
value: last_3_months
properties:
type:
type: string
description: 'This field is required when date_range is present.'
example: preset
enum:
- preset
- all_time
value:
type: string
description: 'This field is required when date_range.type is preset.'
example: last_12_months
enum:
- last_14_days
- last_3_months
- last_12_months
- last_3_years
- all_time
nullable: true
date_range_custom:
type: object
description: 'Optional - Custom date range.'
example:
start: '2025-10-01'
end: '2026-01-27'
properties:
start:
type: string
description: 'This field is required when date_range_custom is present. Must be a valid date.'
example: '2026-02-03T13:58:49'
end:
type: string
description: 'This field is required when date_range_custom is present. Must be a valid date. Must be a date after or equal to date_range_custom.start.'
example: '2052-02-27'
nullable: true
parameters:
-
in: path
name: organisation_uuid
description: 'The organization UUID.'
example: 660e8400-e29b-41d4-a716-446655440001
required: true
schema:
type: string
'/api/organisations/{organisation_uuid}/social':
post:
summary: 'Get Organization Social Posts'
operationId: getOrganizationSocialPosts
description: "Fetch and aggregate social posts from multiple projects/brands within an organization.\nPosts are fetched from external Scraper API, persisted to database, and returned with filters applied.\nSupports filtering by brands, platforms, types, sentiments, keywords, and more."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
posts:
-
uuid: 770e8400-e29b-41d4-a716-446655440002
platform: reddit
community_name: saas
content_type: post
brand_keyword: Acme
title: 'Great product discussion'
text: "We've been using this for months..."
post_date: '2026-01-20T10:00:00.000000Z'
url: 'https://reddit.com/r/saas/comments/123'
sentiment: positive
intensity: 5
score_0_100: 85
user_name: johndoe
upvotes_count: 42
comments_count: 10
project:
uuid: 660e8400-e29b-41d4-a716-446655440001
product_name: 'Acme CRM'
product_logo: 'https://...'
created_at: '2026-01-20T09:30:21.000000Z'
updated_at: '2026-01-20T09:30:21.000000Z'
meta:
current_page: 1
per_page: 10
total: 838
last_page: 17
applied_filters:
brands:
- uuid1
- uuid2
platforms:
- reddit
- github
types:
- post
sentiments:
- positive
- negative
keywords:
- Loom
- Figma
properties:
data:
type: object
properties:
posts:
type: array
example:
-
uuid: 770e8400-e29b-41d4-a716-446655440002
platform: reddit
community_name: saas
content_type: post
brand_keyword: Acme
title: 'Great product discussion'
text: "We've been using this for months..."
post_date: '2026-01-20T10:00:00.000000Z'
url: 'https://reddit.com/r/saas/comments/123'
sentiment: positive
intensity: 5
score_0_100: 85
user_name: johndoe
upvotes_count: 42
comments_count: 10
project:
uuid: 660e8400-e29b-41d4-a716-446655440001
product_name: 'Acme CRM'
product_logo: 'https://...'
created_at: '2026-01-20T09:30:21.000000Z'
updated_at: '2026-01-20T09:30:21.000000Z'
items:
type: object
properties:
uuid:
type: string
example: 770e8400-e29b-41d4-a716-446655440002
platform:
type: string
example: reddit
community_name:
type: string
example: saas
content_type:
type: string
example: post
brand_keyword:
type: string
example: Acme
title:
type: string
example: 'Great product discussion'
text:
type: string
example: "We've been using this for months..."
post_date:
type: string
example: '2026-01-20T10:00:00.000000Z'
url:
type: string
example: 'https://reddit.com/r/saas/comments/123'
sentiment:
type: string
example: positive
intensity:
type: integer
example: 5
score_0_100:
type: integer
example: 85
user_name:
type: string
example: johndoe
upvotes_count:
type: integer
example: 42
comments_count:
type: integer
example: 10
project:
type: object
properties:
uuid:
type: string
example: 660e8400-e29b-41d4-a716-446655440001
product_name:
type: string
example: 'Acme CRM'
product_logo:
type: string
example: 'https://...'
created_at:
type: string
example: '2026-01-20T09:30:21.000000Z'
updated_at:
type: string
example: '2026-01-20T09:30:21.000000Z'
meta:
type: object
properties:
current_page:
type: integer
example: 1
per_page:
type: integer
example: 10
total:
type: integer
example: 838
last_page:
type: integer
example: 17
applied_filters:
type: object
properties:
brands:
type: array
example:
- uuid1
- uuid2
items:
type: string
platforms:
type: array
example:
- reddit
- github
items:
type: string
types:
type: array
example:
- post
items:
type: string
sentiments:
type: array
example:
- positive
- negative
items:
type: string
keywords:
type: array
example:
- Loom
- Figma
items:
type: string
403:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'You do not have access to this organisation.'
properties:
message:
type: string
example: 'You do not have access to this organisation.'
404:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Organisation not found.'
properties:
message:
type: string
example: 'Organisation not found.'
tags:
- 'Social Posts'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
brands:
type: array
description: 'Optional - Filter by brand/project UUIDs. Empty/omitted = all brands.'
example:
- uuid-1
- uuid-2
items:
type: string
view_uuid:
type: string
description: 'Optional - Apply filters from a saved view. When provided, all other filters (except search, page, and per_page) are ignored.'
example: a1b2c3d4-e5f6-g7h8-i9j0
nullable: true
platforms:
type: array
description: 'Optional - Filter by platforms. Ignored if view_uuid is provided.'
example:
- reddit
- github
items:
type: string
types:
type: array
description: 'Optional - Filter by content type. Ignored if view_uuid is provided.'
example:
- post
- comment
items:
type: string
sentiments:
type: array
description: 'Optional - Filter by sentiment. Ignored if view_uuid is provided.'
example:
- positive
- negative
items:
type: string
keywords:
type: array
description: 'Optional - Filter by keywords. Ignored if view_uuid is provided.'
example:
- Loom
- Figma
items:
type: string
date_range:
type: object
description: 'Optional - Preset date range. Ignored if view_uuid is provided.'
example:
type: preset
value: last_3_months
properties:
type:
type: string
description: 'This field is required when date_range is present.'
example: custom
enum:
- preset
- custom
- all_time
value:
type: string
description: 'This field is required when date_range.type is preset.'
example: last_3_years
enum:
- last_14_days
- last_3_months
- last_12_months
- last_3_years
- all_time
nullable: true
date_range_custom:
type: object
description: 'Optional - Custom date range. Ignored if view_uuid is provided.'
example:
start: '2026-01-01'
end: '2026-01-24'
properties:
start:
type: string
description: 'This field is required when date_range_custom is present. Must be a valid date.'
example: '2026-02-03T13:58:49'
end:
type: string
description: 'This field is required when date_range_custom is present. Must be a valid date. Must be a date after or equal to date_range_custom.start.'
example: '2052-02-27'
nullable: true
read_status:
type: string
description: 'Optional - Filter by read status. Ignored if view_uuid is provided.'
example: unread
nullable: true
search:
type: string
description: 'Optional - Keyword search. Can be used with view_uuid to search within saved view filters.'
example: 'customer support'
nullable: true
sort_by:
type: string
description: 'Optional - Sort field. Ignored if view_uuid is provided.'
example: post_date
nullable: true
sort_direction:
type: string
description: 'Optional - Sort direction. Ignored if view_uuid is provided.'
example: desc
nullable: true
page:
type: integer
description: 'Optional - Page number for pagination. Default: 1.'
example: 1
nullable: true
per_page:
type: integer
description: 'Optional - Items per page. Default: 10. Maximum: 100.'
example: 10
nullable: true
parameters:
-
in: path
name: organisation_uuid
description: 'The organization UUID.'
example: 660e8400-e29b-41d4-a716-446655440001
required: true
schema:
type: string
'/api/organisations/{organisation_uuid}/social/analytics':
post:
summary: 'Organisation-level social analytics.'
operationId: organisationLevelSocialAnalytics
description: "Returns mention count and average sentiment with period-over-period comparison.\nFilters: brands (social_posts.brand_id), platforms, date_range, date_range_custom (start/end, same as social posts), or custom_date_range (from/to)."
parameters: []
responses: { }
tags:
- 'Social Posts'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
brands:
type: array
description: ''
example:
- 16
items:
type: integer
platforms:
type: array
description: ''
example:
- reddit
items:
type: string
enum:
- reddit
- github
- stackoverflow
- hackernews
date_range:
type: object
description: ''
example: null
properties:
type:
type: string
description: 'This field is required when date_range is present.'
example: all_time
enum:
- preset
- all_time
value:
type: string
description: 'This field is required when date_range.type is preset.'
example: last_3_years
enum:
- last_14_days
- last_3_months
- last_12_months
- last_3_years
- all_time
nullable: true
custom_date_range:
type: object
description: ''
example: null
properties:
from:
type: string
description: 'This field is required when custom_date_range is present. Must be a valid date.'
example: '2026-02-03T13:58:49'
to:
type: string
description: 'This field is required when custom_date_range is present. Must be a valid date. Must be a date after or equal to custom_date_range.from.'
example: '2052-02-27'
nullable: true
date_range_custom:
type: object
description: ''
example: null
properties:
start:
type: string
description: 'This field is required when date_range_custom is present. Must be a valid date.'
example: '2026-02-03T13:58:49'
end:
type: string
description: 'This field is required when date_range_custom is present. Must be a valid date. Must be a date after or equal to date_range_custom.start.'
example: '2052-02-27'
nullable: true
required:
- platforms
security: []
parameters:
-
in: path
name: organisation_uuid
description: ''
example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c
required: true
schema:
type: string
'/api/organisations/{organisation_uuid}/social/views':
get:
summary: 'List Social Post Views'
operationId: listSocialPostViews
description: 'Get all saved filter views for the current user in this organization.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
uuid: a1b2c3d4-e5f6-g7h8-i9j0
name: 'Critical Alerts'
filters:
platforms:
- reddit
sentiments:
- negative
created_at: '2026-01-26T10:00:00.000000Z'
properties:
data:
type: array
example:
-
uuid: a1b2c3d4-e5f6-g7h8-i9j0
name: 'Critical Alerts'
filters:
platforms:
- reddit
sentiments:
- negative
created_at: '2026-01-26T10:00:00.000000Z'
items:
type: object
properties:
uuid:
type: string
example: a1b2c3d4-e5f6-g7h8-i9j0
name:
type: string
example: 'Critical Alerts'
filters:
type: object
properties:
platforms:
type: array
example:
- reddit
items:
type: string
sentiments:
type: array
example:
- negative
items:
type: string
created_at:
type: string
example: '2026-01-26T10:00:00.000000Z'
tags:
- 'Social Posts'
post:
summary: 'Store Social Post View'
operationId: storeSocialPostView
description: 'Create a new saved filter view for social posts.'
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
data:
uuid: a1b2c3d4-e5f6-g7h8-i9j0
name: 'Critical Alerts'
filters:
platforms:
- reddit
sentiments:
- negative
created_at: '2026-01-26T10:00:00.000000Z'
properties:
data:
type: object
properties:
uuid:
type: string
example: a1b2c3d4-e5f6-g7h8-i9j0
name:
type: string
example: 'Critical Alerts'
filters:
type: object
properties:
platforms:
type: array
example:
- reddit
items:
type: string
sentiments:
type: array
example:
- negative
items:
type: string
created_at:
type: string
example: '2026-01-26T10:00:00.000000Z'
tags:
- 'Social Posts'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The name of the view.'
example: 'Critical Alerts'
filters:
type: object
description: 'Filter configuration.'
example:
platforms:
- reddit
sentiments:
- negative
properties:
brands:
type: array
description: 'Must be a valid UUID. The uuid of an existing record in the projects table. The uuid of an existing record in the projects table.'
example:
- 6ff8f7f6-1eb3-3525-be4a-3932c805afed
items:
type: string
platforms:
type: array
description: ''
example:
- reddit
items:
type: string
enum:
- reddit
- github
- stackoverflow
- hackernews
types:
type: array
description: ''
example:
- comment
items:
type: string
enum:
- post
- comment
sentiments:
type: array
description: ''
example:
- positive
items:
type: string
enum:
- positive
- neutral
- negative
keywords:
type: array
description: 'Must not be greater than 255 characters.'
example:
- g
items:
type: string
date_range:
type: object
description: ''
example: null
properties:
type:
type: string
description: 'This field is required when filters.date_range is present.'
example: custom
enum:
- preset
- custom
- all_time
value:
type: string
description: 'This field is required when filters.date_range.type is preset.'
example: all_time
enum:
- last_14_days
- last_3_months
- last_12_months
- last_3_years
- all_time
nullable: true
date_range_custom:
type: object
description: ''
example: null
properties:
start:
type: string
description: 'This field is required when filters.date_range_custom is present. Must be a valid date.'
example: '2026-02-03T13:58:49'
end:
type: string
description: 'This field is required when filters.date_range_custom is present. Must be a valid date. Must be a date after or equal to filters.date_range_custom.start.'
example: '2052-02-27'
nullable: true
read_status:
type: string
description: ''
example: unread
enum:
- all
- read
- unread
nullable: true
search:
type: string
description: 'Must not be greater than 500 characters.'
example: 'n'
nullable: true
sort_by:
type: string
description: ''
example: comments_count
enum:
- post_date
- sentiment
- platform
- upvotes_count
- comments_count
- created_at
nullable: true
sort_direction:
type: string
description: ''
example: asc
enum:
- asc
- desc
nullable: true
required:
- name
- filters
parameters:
-
in: path
name: organisation_uuid
description: 'The organization UUID.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
required: true
schema:
type: string
'/api/organisations/{organisation_uuid}/social/views/{view_uuid}':
get:
summary: 'Show Social Post View'
operationId: showSocialPostView
description: 'Get a specific saved filter view.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
uuid: a1b2c3d4-e5f6-g7h8-i9j0
name: 'Critical Alerts'
filters:
platforms:
- reddit
sentiments:
- negative
created_at: '2026-01-26T10:00:00.000000Z'
properties:
data:
type: object
properties:
uuid:
type: string
example: a1b2c3d4-e5f6-g7h8-i9j0
name:
type: string
example: 'Critical Alerts'
filters:
type: object
properties:
platforms:
type: array
example:
- reddit
items:
type: string
sentiments:
type: array
example:
- negative
items:
type: string
created_at:
type: string
example: '2026-01-26T10:00:00.000000Z'
tags:
- 'Social Posts'
put:
summary: 'Update Social Post View'
operationId: updateSocialPostView
description: 'Update a saved filter view.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
uuid: a1b2c3d4-e5f6-g7h8-i9j0
name: 'Critical Alerts Updated'
filters:
platforms:
- reddit
sentiments:
- negative
updated_at: '2026-01-26T11:00:00.000000Z'
properties:
data:
type: object
properties:
uuid:
type: string
example: a1b2c3d4-e5f6-g7h8-i9j0
name:
type: string
example: 'Critical Alerts Updated'
filters:
type: object
properties:
platforms:
type: array
example:
- reddit
items:
type: string
sentiments:
type: array
example:
- negative
items:
type: string
updated_at:
type: string
example: '2026-01-26T11:00:00.000000Z'
tags:
- 'Social Posts'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
sort_order:
type: integer
description: 'Must be at least 0.'
example: 27
name:
type: string
description: 'optional The name of the view.'
example: architecto
filters:
type: object
description: 'optional Filter configuration.'
example: []
properties:
brands:
type: array
description: 'Must be a valid UUID. The uuid of an existing record in the projects table. The uuid of an existing record in the projects table.'
example:
- 6b72fe4a-5b40-307c-bc24-f79acf9a1bb9
items:
type: string
platforms:
type: array
description: ''
example:
- github
items:
type: string
enum:
- reddit
- github
- stackoverflow
- hackernews
types:
type: array
description: ''
example:
- comment
items:
type: string
enum:
- post
- comment
sentiments:
type: array
description: ''
example:
- neutral
items:
type: string
enum:
- positive
- neutral
- negative
keywords:
type: array
description: 'Must not be greater than 255 characters.'
example:
- m
items:
type: string
date_range:
type: object
description: ''
example: null
properties:
type:
type: string
description: 'This field is required when filters.date_range is present.'
example: all_time
enum:
- preset
- custom
- all_time
value:
type: string
description: 'This field is required when filters.date_range.type is preset.'
example: last_3_months
enum:
- last_14_days
- last_3_months
- last_12_months
- last_3_years
- all_time
nullable: true
date_range_custom:
type: object
description: ''
example: null
properties:
start:
type: string
description: 'This field is required when filters.date_range_custom is present. Must be a valid date.'
example: '2026-02-03T13:58:49'
end:
type: string
description: 'This field is required when filters.date_range_custom is present. Must be a valid date. Must be a date after or equal to filters.date_range_custom.start.'
example: '2052-02-27'
nullable: true
read_status:
type: string
description: ''
example: all
enum:
- all
- read
- unread
nullable: true
search:
type: string
description: 'Must not be greater than 500 characters.'
example: 'n'
nullable: true
sort_by:
type: string
description: ''
example: platform
enum:
- post_date
- sentiment
- platform
- upvotes_count
- comments_count
- created_at
nullable: true
sort_direction:
type: string
description: ''
example: asc
enum:
- asc
- desc
nullable: true
delete:
summary: 'Delete Social Post View'
operationId: deleteSocialPostView
description: 'Delete a saved filter view.'
parameters: []
responses:
204:
description: ''
content:
application/json:
schema:
type: object
example: { }
properties: { }
tags:
- 'Social Posts'
parameters:
-
in: path
name: organisation_uuid
description: 'The organization UUID.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
required: true
schema:
type: string
-
in: path
name: view_uuid
description: 'The view UUID.'
example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed
required: true
schema:
type: string
/api/webhook:
post:
summary: ''
operationId: postApiWebhook
description: ''
parameters: []
responses: { }
tags:
- Endpoints
security: []
/api/invites/validate:
post:
summary: 'Validate Invite'
operationId: validateInvite
description: "Validate an invite token and return the associated email if the invite is valid.\nThis endpoint is public and does not require authentication."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
email: user@example.com
properties:
email:
type: string
example: user@example.com
404:
description: 'Invite not found'
content:
application/json:
schema:
type: object
example:
message: 'Invite not found.'
properties:
message:
type: string
example: 'Invite not found.'
410:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Invite expired'
type: object
example:
message: 'This invite has expired.'
properties:
message:
type: string
example: 'This invite has expired.'
-
description: 'Invite no longer valid'
type: object
example:
message: 'This invite is no longer valid.'
properties:
message:
type: string
example: 'This invite is no longer valid.'
tags:
- Invites
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
token:
type: string
description: 'The invite token.'
example: abc123def456...
required:
- token
security: []
/api/invites:
get:
summary: 'List Invites'
operationId: listInvites
description: "Get invites based on context:\n- Organisation level (no project_id): Returns all organisation invites + all project invites for the organisation\n- Project level (with project_id): Returns only invites for the specified project"
parameters:
-
in: query
name: project_id
description: 'Optional. Filter invites by project UUID.'
example: 660e8400-e29b-41d4-a716-446655440001
required: false
schema:
type: string
description: 'Optional. Filter invites by project UUID.'
example: 660e8400-e29b-41d4-a716-446655440001
responses:
200:
description: ''
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
example: 1
email:
type: string
example: user@example.com
type:
type: string
example: organisation
status:
type: string
example: pending
expires_at:
type: string
example: '2025-12-31T10:00:00.000000Z'
organisation_id:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
organisation:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Corp'
project_id:
type: string
example: null
project:
type: string
example: null
inviter:
type: object
properties:
name:
type: string
example: 'John Admin'
email:
type: string
example: admin@acme.com
created_at:
type: string
example: '2025-12-24T10:00:00.000000Z'
updated_at:
type: string
example: '2025-12-24T10:00:00.000000Z'
example:
-
id: 1
email: user@example.com
type: organisation
status: pending
expires_at: '2025-12-31T10:00:00.000000Z'
organisation_id: 550e8400-e29b-41d4-a716-446655440000
organisation:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Corp'
project_id: null
project: null
inviter:
name: 'John Admin'
email: admin@acme.com
created_at: '2025-12-24T10:00:00.000000Z'
updated_at: '2025-12-24T10:00:00.000000Z'
-
id: 2
email: developer@example.com
type: project
status: pending
expires_at: '2025-12-31T10:00:00.000000Z'
organisation_id: null
organisation: null
project_id: 660e8400-e29b-41d4-a716-446655440001
project:
uuid: 660e8400-e29b-41d4-a716-446655440001
name: 'My Product'
inviter:
name: 'John Admin'
email: admin@acme.com
created_at: '2025-12-24T10:00:00.000000Z'
updated_at: '2025-12-24T10:00:00.000000Z'
403:
description: 'No access to project'
content:
application/json:
schema:
type: object
example:
message: 'You do not have access to this project.'
properties:
message:
type: string
example: 'You do not have access to this project.'
404:
description: 'Project not found'
content:
application/json:
schema:
type: object
example:
message: 'Project not found.'
properties:
message:
type: string
example: 'Project not found.'
422:
description: 'No organisation context'
content:
application/json:
schema:
type: object
example:
message: 'No organisation context found.'
errors:
organisation:
- 'Please select an organisation or set current organisation.'
properties:
message:
type: string
example: 'No organisation context found.'
errors:
type: object
properties:
organisation:
type: array
example:
- 'Please select an organisation or set current organisation.'
items:
type: string
tags:
- Invites
post:
summary: 'Create Invite'
operationId: createInvite
description: "Create a new invite for a user to join an organisation or project.\nFor organisation invites, the organisation is automatically derived from the authenticated user's current organisation context."
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Invite created successfully.'
data:
id: 1
email: user@example.com
type: organisation
status: pending
token: abc123...
expires_at: '2025-12-31T10:00:00.000000Z'
organisation_id: 550e8400-e29b-41d4-a716-446655440000
project_id: null
created_at: '2025-12-24T10:00:00.000000Z'
updated_at: '2025-12-24T10:00:00.000000Z'
properties:
message:
type: string
example: 'Invite created successfully.'
data:
type: object
properties:
id:
type: integer
example: 1
email:
type: string
example: user@example.com
type:
type: string
example: organisation
status:
type: string
example: pending
token:
type: string
example: abc123...
expires_at:
type: string
example: '2025-12-31T10:00:00.000000Z'
organisation_id:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
project_id:
type: string
example: null
created_at:
type: string
example: '2025-12-24T10:00:00.000000Z'
updated_at:
type: string
example: '2025-12-24T10:00:00.000000Z'
422:
description: 'No organisation context'
content:
application/json:
schema:
type: object
example:
message: 'No organisation context found.'
errors:
organisation:
- 'Please select an organisation or set current organisation.'
properties:
message:
type: string
example: 'No organisation context found.'
errors:
type: object
properties:
organisation:
type: array
example:
- 'Please select an organisation or set current organisation.'
items:
type: string
tags:
- Invites
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
description: 'The email address to send the invite to.'
example: user@example.com
type:
type: string
description: 'The type of invite (organisation or project).'
example: organisation
project_id:
type: string
description: 'The project UUID (required for project invites).'
example: 660e8400-e29b-41d4-a716-446655440001
nullable: true
required:
- email
- type
'/api/invites/{invite_id}':
delete:
summary: 'Delete Invite'
operationId: deleteInvite
description: 'Delete an invite. Only the inviter or organisation owners can delete invites.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Invite deleted successfully.'
properties:
message:
type: string
example: 'Invite deleted successfully.'
403:
description: 'Not authorized'
content:
application/json:
schema:
type: object
example:
message: 'You are not authorized to delete this invite.'
properties:
message:
type: string
example: 'You are not authorized to delete this invite.'
404:
description: 'Invite not found'
content:
application/json:
schema:
type: object
example:
message: 'Invite not found.'
properties:
message:
type: string
example: 'Invite not found.'
422:
description: 'No organisation context'
content:
application/json:
schema:
type: object
example:
message: 'No organisation context found.'
errors:
organisation:
- 'Please select an organisation or set current organisation.'
properties:
message:
type: string
example: 'No organisation context found.'
errors:
type: object
properties:
organisation:
type: array
example:
- 'Please select an organisation or set current organisation.'
items:
type: string
tags:
- Invites
parameters:
-
in: path
name: invite_id
description: 'The invite ID.'
example: 1
required: true
schema:
type: integer
'/api/projects/{project_uuid}/links':
get:
summary: 'List Project Links'
operationId: listProjectLinks
description: 'Get all links associated with a specific project.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
uuid: 550e8400-e29b-41d4-a716-446655440000
platform: g2
url: 'https://g2.com/products/acme'
enabled: true
created_at: '2026-01-22T10:00:00.000000Z'
updated_at: '2026-01-22T10:00:00.000000Z'
properties:
data:
type: array
example:
-
uuid: 550e8400-e29b-41d4-a716-446655440000
platform: g2
url: 'https://g2.com/products/acme'
enabled: true
created_at: '2026-01-22T10:00:00.000000Z'
updated_at: '2026-01-22T10:00:00.000000Z'
items:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
platform:
type: string
example: g2
url:
type: string
example: 'https://g2.com/products/acme'
enabled:
type: boolean
example: true
created_at:
type: string
example: '2026-01-22T10:00:00.000000Z'
updated_at:
type: string
example: '2026-01-22T10:00:00.000000Z'
403:
description: 'No Access'
content:
application/json:
schema:
type: object
example:
message: 'You do not have access to this project.'
properties:
message:
type: string
example: 'You do not have access to this project.'
404:
description: 'Project Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Project not found.'
properties:
message:
type: string
example: 'Project not found.'
tags:
- Links
parameters:
-
in: path
name: project_uuid
description: 'The project UUID.'
example: 660e8400-e29b-41d4-a716-446655440001
required: true
schema:
type: string
/api/notifications:
get:
summary: 'List Notifications'
operationId: listNotifications
description: 'Get all notification rules for the current organisation.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: array
items:
type: object
properties:
uuid:
type: string
example: 770e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Daily Review Alerts'
trigger_type:
type: string
example: new_review
languages:
type: array
example:
- en
- es
items:
type: string
auto_translate:
type: boolean
example: true
rating_filters:
type: array
example:
- 1
- 2
- 3
items:
type: integer
sentiment_filters:
type: array
example:
- negative
items:
type: string
condition_filters:
type: object
properties:
logic:
type: string
example: and
rules:
type: array
example:
-
field: platforms
operator: contains
value:
- g2
items:
type: object
properties:
field:
type: string
example: platforms
operator:
type: string
example: contains
value:
type: array
example:
- g2
items:
type: string
notification_frequency:
type: string
example: instant
read_status:
type: string
example: all
is_active:
type: boolean
example: true
channels:
type: array
example: []
projects:
type: array
example: []
created_at:
type: string
example: '2025-12-31T12:00:00.000000Z'
updated_at:
type: string
example: '2025-12-31T12:00:00.000000Z'
example:
-
uuid: 770e8400-e29b-41d4-a716-446655440000
name: 'Daily Review Alerts'
trigger_type: new_review
languages:
- en
- es
auto_translate: true
rating_filters:
- 1
- 2
- 3
sentiment_filters:
- negative
condition_filters:
logic: and
rules:
-
field: platforms
operator: contains
value:
- g2
notification_frequency: instant
read_status: all
is_active: true
channels: []
projects: []
created_at: '2025-12-31T12:00:00.000000Z'
updated_at: '2025-12-31T12:00:00.000000Z'
tags:
- Notifications
post:
summary: 'Create Notification'
operationId: createNotification
description: 'Create a new notification rule with channels and project mappings.'
parameters: []
responses:
201:
description: Success
content:
application/json:
schema:
type: object
example:
message: 'Notification created successfully.'
data:
uuid: 770e8400-e29b-41d4-a716-446655440000
name: 'Daily Review Alerts'
trigger_type: new_review
languages:
- en
- es
auto_translate: true
rating_filters:
- 1
- 2
- 3
- 4
- 5
sentiment_filters:
- positive
- neutral
- negative
condition_filters:
logic: and
rules:
-
field: platforms
operator: contains
value:
- g2
- capterra
-
field: sentiment
operator: contains
value:
- negative
notification_frequency: instant
read_status: unread
keyword:
- ai
- support
content_type: post
is_active: true
channels:
-
uuid: 880e8400-e29b-41d4-a716-446655440001
channel_type: slack
is_active: true
config:
channel_names:
- alerts
- incidents
channel_url: 'https://hooks.slack.com/services/xxx'
recipients:
- alerts@company.com
url: 'https://your-server.com/webhook/notifications'
projects:
-
uuid: 990e8400-e29b-41d4-a716-446655440002
project_uuid: 5f1812b1-15f7-432d-996a-9ab5cfbe01d6
project_name: 'Acme App'
is_active: true
product_logo: 'https://cdn.example.com/logo.png'
platforms:
review:
- g2
- capterra
social:
- reddit
created_at: '2025-12-31T12:00:00.000000Z'
updated_at: '2025-12-31T12:00:00.000000Z'
properties:
message:
type: string
example: 'Notification created successfully.'
data:
type: object
properties:
uuid:
type: string
example: 770e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Daily Review Alerts'
trigger_type:
type: string
example: new_review
languages:
type: array
example:
- en
- es
items:
type: string
auto_translate:
type: boolean
example: true
rating_filters:
type: array
example:
- 1
- 2
- 3
- 4
- 5
items:
type: integer
sentiment_filters:
type: array
example:
- positive
- neutral
- negative
items:
type: string
condition_filters:
type: object
properties:
logic:
type: string
example: and
rules:
type: array
example:
-
field: platforms
operator: contains
value:
- g2
- capterra
-
field: sentiment
operator: contains
value:
- negative
items:
type: object
properties:
field:
type: string
example: platforms
operator:
type: string
example: contains
value:
type: array
example:
- g2
- capterra
items:
type: string
notification_frequency:
type: string
example: instant
read_status:
type: string
example: unread
keyword:
type: array
example:
- ai
- support
items:
type: string
content_type:
type: string
example: post
is_active:
type: boolean
example: true
channels:
type: array
example:
-
uuid: 880e8400-e29b-41d4-a716-446655440001
channel_type: slack
is_active: true
config:
channel_names:
- alerts
- incidents
channel_url: 'https://hooks.slack.com/services/xxx'
recipients:
- alerts@company.com
url: 'https://your-server.com/webhook/notifications'
items:
type: object
properties:
uuid:
type: string
example: 880e8400-e29b-41d4-a716-446655440001
channel_type:
type: string
example: slack
is_active:
type: boolean
example: true
config:
type: object
properties:
channel_names:
type: array
example:
- alerts
- incidents
items:
type: string
channel_url:
type: string
example: 'https://hooks.slack.com/services/xxx'
recipients:
type: array
example:
- alerts@company.com
items:
type: string
url:
type: string
example: 'https://your-server.com/webhook/notifications'
projects:
type: array
example:
-
uuid: 990e8400-e29b-41d4-a716-446655440002
project_uuid: 5f1812b1-15f7-432d-996a-9ab5cfbe01d6
project_name: 'Acme App'
is_active: true
product_logo: 'https://cdn.example.com/logo.png'
platforms:
review:
- g2
- capterra
social:
- reddit
items:
type: object
properties:
uuid:
type: string
example: 990e8400-e29b-41d4-a716-446655440002
project_uuid:
type: string
example: 5f1812b1-15f7-432d-996a-9ab5cfbe01d6
project_name:
type: string
example: 'Acme App'
is_active:
type: boolean
example: true
product_logo:
type: string
example: 'https://cdn.example.com/logo.png'
platforms:
type: object
properties:
review:
type: array
example:
- g2
- capterra
items:
type: string
social:
type: array
example:
- reddit
items:
type: string
created_at:
type: string
example: '2025-12-31T12:00:00.000000Z'
updated_at:
type: string
example: '2025-12-31T12:00:00.000000Z'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The name field is required. (and 2 more errors)'
errors:
name:
- 'The name field is required.'
channels:
- 'At least one channel is required.'
projects:
- 'Select at least one brand.'
properties:
message:
type: string
example: 'The name field is required. (and 2 more errors)'
errors:
type: object
properties:
name:
type: array
example:
- 'The name field is required.'
items:
type: string
channels:
type: array
example:
- 'At least one channel is required.'
items:
type: string
projects:
type: array
example:
- 'Select at least one brand.'
items:
type: string
tags:
- Notifications
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The notification name.'
example: 'Daily Review Alerts'
trigger_type:
type: string
description: 'The trigger type. Defaults to new_review.'
example: new_review
is_active:
type: boolean
description: 'Whether the notification is active. Defaults to true.'
example: true
languages:
type: array
description: 'Language filters (empty = all languages).'
example:
- en
- es
- fr
- de
items:
type: string
auto_translate:
type: boolean
description: 'Auto-translate to English. Defaults to false.'
example: true
rating_filters:
type: array
description: 'Rating filters 1-5 (empty = all stars).'
example:
- 1
- 2
- 3
- 4
- 5
items:
type: integer
sentiment_filters:
type: array
description: 'Sentiment filters (empty = all).'
example:
- positive
- neutral
- negative
items:
type: string
keyword:
type: array
description: 'Keyword filters (empty or omitted = all).'
example:
- ai
- support
items:
type: string
content_type:
type: string
description: 'Content type: post, comment, all. Defaults to all.'
example: post
notification_frequency:
type: string
description: 'Notification delivery frequency: instant, daily. Defaults to instant.'
example: instant
read_status:
type: string
description: 'Filter by read status: all, read, unread. Defaults to all.'
example: all
condition_filters:
type: object
description: 'Advanced condition filters with AND/OR logic.'
example: []
properties:
logic:
type: string
description: 'Logic operator for combining rules: and, or.'
example: and
rules:
type: array
description: 'Array of filter rules or nested groups.'
example:
- []
items:
type: object
properties:
field:
type: string
description: 'Filter field: brands, platforms, read_status, keywords, type, sentiment, content, user.'
example: platforms
operator:
type: string
description: 'Filter operator: contains, not_contains.'
example: contains
value:
type: mixed
description: 'Filter value (string or array depending on field).'
example: '["g2", "capterra"]'
logic:
type: string
description: 'For nested groups: and, or.'
example: or
rules:
type: array
description: 'Nested rules array for grouped conditions.'
example:
- []
items:
type: object
required:
- logic
- rules
nullable: true
channels:
type: array
description: 'Delivery channels configuration. At least one required.'
example:
- []
items:
type: object
properties:
channel_type:
type: string
description: 'Channel type: slack, email, ms_teams, lark, webhook.'
example: slack
is_active:
type: boolean
description: 'Whether channel is active.'
example: true
config:
type: object
description: 'Channel-specific configuration.'
example: []
properties:
channel_names:
type: array
description: 'Slack channel names.'
example:
- alerts
- incidents
items:
type: string
channel_url:
type: string
description: 'Slack channel webhook URL.'
example: 'https://hooks.slack.com/services/xxx'
nullable: true
recipients:
type: array
description: 'Email recipients (required if email channel is active).'
example:
- alerts@company.com
items:
type: string
url:
type: string
description: 'Custom webhook URL (required if webhook channel is active).'
example: 'https://your-server.com/webhook/notifications'
nullable: true
nullable: true
required:
- channel_type
projects:
type: array
description: 'Project/brand mappings. At least one required.'
example:
- []
items:
type: object
properties:
project_uuid:
type: string
description: 'The project UUID.'
example: 5f1812b1-15f7-432d-996a-9ab5cfbe01d6
is_active:
type: boolean
description: 'Whether project mapping is active.'
example: true
platforms:
type: object
description: 'Platform selections for this project.'
example: []
properties:
review:
type: array
description: 'Review platforms to monitor. Supported: g2, capterra, software_advice, trustpilot, omr_reviews, clutch, sourceforge, product_hunt, hubspot_directory, goodfirms, chrome_web_store, google_workspace_marketplace, subscribed_fyi, google_maps, google_play, apple_app_store, play_store, app_store, google_reviews, gartner, getapp, yotpo, product_review, trustradius.'
example:
- g2
- capterra
- product_hunt
- google_play
items:
type: string
social:
type: array
description: 'Social platforms to monitor.'
example:
- reddit
- hackernews
- linkedin
items:
type: string
nullable: true
product_logo:
type: string
description: 'Logo URL for the project (optional).'
example: 'https://cdn.example.com/logo.png'
nullable: true
required:
- project_uuid
required:
- name
- channels
- projects
'/api/notifications/{uuid}':
get:
summary: 'Get Notification'
operationId: getNotification
description: 'Get details of a specific notification.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
uuid: 770e8400-e29b-41d4-a716-446655440000
name: 'Daily Review Alerts'
trigger_type: new_review
languages:
- en
- es
auto_translate: true
rating_filters:
- 1
- 2
- 3
sentiment_filters:
- negative
condition_filters:
logic: and
rules:
-
field: platforms
operator: contains
value:
- g2
notification_frequency: instant
read_status: all
is_active: true
channels: []
projects: []
created_at: '2025-12-31T12:00:00.000000Z'
updated_at: '2025-12-31T12:00:00.000000Z'
properties:
uuid:
type: string
example: 770e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Daily Review Alerts'
trigger_type:
type: string
example: new_review
languages:
type: array
example:
- en
- es
items:
type: string
auto_translate:
type: boolean
example: true
rating_filters:
type: array
example:
- 1
- 2
- 3
items:
type: integer
sentiment_filters:
type: array
example:
- negative
items:
type: string
condition_filters:
type: object
properties:
logic:
type: string
example: and
rules:
type: array
example:
-
field: platforms
operator: contains
value:
- g2
items:
type: object
properties:
field:
type: string
example: platforms
operator:
type: string
example: contains
value:
type: array
example:
- g2
items:
type: string
notification_frequency:
type: string
example: instant
read_status:
type: string
example: all
is_active:
type: boolean
example: true
channels:
type: array
example: []
projects:
type: array
example: []
created_at:
type: string
example: '2025-12-31T12:00:00.000000Z'
updated_at:
type: string
example: '2025-12-31T12:00:00.000000Z'
tags:
- Notifications
put:
summary: 'Update Notification'
operationId: updateNotification
description: 'Update an existing notification rule with channels and project mappings.'
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
message: 'Notification updated successfully.'
data:
uuid: 770e8400-e29b-41d4-a716-446655440000
name: 'Updated Review Alerts'
trigger_type: new_review
languages:
- en
auto_translate: false
rating_filters:
- 1
- 2
sentiment_filters:
- negative
condition_filters:
logic: and
rules:
-
field: sentiment
operator: contains
value:
- negative
notification_frequency: daily
read_status: unread
keyword:
- ai
- support
content_type: comment
is_active: true
channels: []
projects: []
created_at: '2025-12-31T12:00:00.000000Z'
updated_at: '2025-12-31T13:00:00.000000Z'
properties:
message:
type: string
example: 'Notification updated successfully.'
data:
type: object
properties:
uuid:
type: string
example: 770e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Updated Review Alerts'
trigger_type:
type: string
example: new_review
languages:
type: array
example:
- en
items:
type: string
auto_translate:
type: boolean
example: false
rating_filters:
type: array
example:
- 1
- 2
items:
type: integer
sentiment_filters:
type: array
example:
- negative
items:
type: string
condition_filters:
type: object
properties:
logic:
type: string
example: and
rules:
type: array
example:
-
field: sentiment
operator: contains
value:
- negative
items:
type: object
properties:
field:
type: string
example: sentiment
operator:
type: string
example: contains
value:
type: array
example:
- negative
items:
type: string
notification_frequency:
type: string
example: daily
read_status:
type: string
example: unread
keyword:
type: array
example:
- ai
- support
items:
type: string
content_type:
type: string
example: comment
is_active:
type: boolean
example: true
channels:
type: array
example: []
projects:
type: array
example: []
created_at:
type: string
example: '2025-12-31T12:00:00.000000Z'
updated_at:
type: string
example: '2025-12-31T13:00:00.000000Z'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Notification not found.'
properties:
message:
type: string
example: 'Notification not found.'
tags:
- Notifications
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The notification name.'
example: 'Daily Review Alerts'
trigger_type:
type: string
description: 'The trigger type.'
example: new_review
is_active:
type: boolean
description: 'Whether the notification is active.'
example: true
languages:
type: array
description: 'Language filters (empty = all languages).'
example:
- en
- es
- fr
- de
items:
type: string
auto_translate:
type: boolean
description: 'Auto-translate to English.'
example: true
rating_filters:
type: array
description: 'Rating filters 1-5.'
example:
- 1
- 2
- 3
- 4
- 5
items:
type: integer
sentiment_filters:
type: array
description: 'Sentiment filters.'
example:
- positive
- neutral
- negative
items:
type: string
keyword:
type: array
description: 'Keyword filters (empty or omitted = all).'
example:
- ai
- support
items:
type: string
content_type:
type: string
description: 'Content type: post, comment, all. Defaults to all.'
example: post
notification_frequency:
type: string
description: 'Notification delivery frequency: instant, daily.'
example: instant
read_status:
type: string
description: 'Filter by read status: all, read, unread.'
example: all
condition_filters:
type: object
description: 'Advanced condition filters with AND/OR logic.'
example: []
properties:
logic:
type: string
description: 'Logic operator for combining rules: and, or.'
example: and
rules:
type: array
description: 'Array of filter rules or nested groups.'
example:
- []
items:
type: object
properties:
field:
type: string
description: 'Filter field: brands, platforms, read_status, keywords, type, sentiment, content, user.'
example: platforms
operator:
type: string
description: 'Filter operator: contains, not_contains.'
example: contains
value:
type: mixed
description: 'Filter value (string or array depending on field).'
example: '["g2", "capterra"]'
required:
- logic
- rules
nullable: true
channels:
type: array
description: 'Delivery channels configuration.'
example:
- []
items:
type: object
properties:
channel_type:
type: string
description: 'Channel type: slack, email, ms_teams, lark, webhook.'
example: slack
is_active:
type: boolean
description: 'Whether channel is active.'
example: true
config:
type: object
description: 'Channel-specific configuration.'
example: []
properties:
channel_names:
type: array
description: 'Slack channel names.'
example:
- alerts
- incidents
items:
type: string
channel_url:
type: string
description: 'Slack channel webhook URL.'
example: 'https://hooks.slack.com/services/xxx'
nullable: true
recipients:
type: array
description: 'Email recipients (required if email channel is active).'
example:
- alerts@company.com
items:
type: string
url:
type: string
description: 'Custom webhook URL (required if webhook channel is active).'
example: 'https://your-server.com/webhook/notifications'
nullable: true
nullable: true
required:
- channel_type
projects:
type: array
description: 'Project/brand mappings.'
example:
- []
items:
type: object
properties:
project_uuid:
type: string
description: 'The project UUID.'
example: 5f1812b1-15f7-432d-996a-9ab5cfbe01d6
is_active:
type: boolean
description: 'Whether project mapping is active.'
example: true
platforms:
type: object
description: 'Platform selections for this project.'
example: []
properties:
review:
type: array
description: ''
example:
- software_advice
items:
type: string
enum:
- g2
- capterra
- trustpilot
- trustradius
- product_hunt
- gartner
- getapp
- yotpo
- software_advice
- omr_reviews
- product_review
- subscribed_fyi
- app_store
- play_store
- google_reviews
- clutch
- sourceforge
- hubspot_directory
- goodfirms
- chrome_web_store
- google_workspace_marketplace
- google_maps
- google_play
- apple_app_store
social:
type: array
description: ''
example:
- reddit
items:
type: string
enum:
- reddit
- github
- stackoverflow
- hackernews
- youtube
- linkedin
- x
nullable: true
product_logo:
type: string
description: 'Must not be greater than 2048 characters.'
example: g
nullable: true
required:
- project_uuid
required:
- name
- channels
- projects
delete:
summary: 'Delete Notification'
operationId: deleteNotification
description: 'Delete a notification and all associated channels and project mappings.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Notification deleted successfully.'
properties:
message:
type: string
example: 'Notification deleted successfully.'
404:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Notification not found.'
properties:
message:
type: string
example: 'Notification not found.'
tags:
- Notifications
parameters:
-
in: path
name: uuid
description: 'The notification UUID.'
example: 770e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
/api/onboarding:
post:
summary: 'Complete Onboarding'
operationId: completeOnboarding
description: "Create an organisation and project in a single step during onboarding.\nThe authenticated user becomes the organisation owner."
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Onboarding completed successfully.'
data:
organisation_uuid: 550e8400-e29b-41d4-a716-446655440000
product_uuid: 660e8400-e29b-41d4-a716-446655440001
organisation:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Inc'
website: 'https://acme.com'
project:
uuid: 660e8400-e29b-41d4-a716-446655440001
product_name: 'Acme CRM'
product_website: 'https://acme.com'
product_logo: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID'
review_platforms: { }
reddit_keywords:
- acme
- 'acme crm'
reddit_brand_name: Acme
scraper_sync:
success: true
synced_platforms:
- g2
- capterra
errors: []
brand_sync:
success: true
brand_id: '12345'
error: null
properties:
message:
type: string
example: 'Onboarding completed successfully.'
data:
type: object
properties:
organisation_uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
product_uuid:
type: string
example: 660e8400-e29b-41d4-a716-446655440001
organisation:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Inc'
website:
type: string
example: 'https://acme.com'
project:
type: object
properties:
uuid:
type: string
example: 660e8400-e29b-41d4-a716-446655440001
product_name:
type: string
example: 'Acme CRM'
product_website:
type: string
example: 'https://acme.com'
product_logo:
type: string
example: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID'
review_platforms:
type: object
properties: { }
reddit_keywords:
type: array
example:
- acme
- 'acme crm'
items:
type: string
reddit_brand_name:
type: string
example: Acme
scraper_sync:
type: object
properties:
success:
type: boolean
example: true
synced_platforms:
type: array
example:
- g2
- capterra
items:
type: string
errors:
type: array
example: []
brand_sync:
type: object
properties:
success:
type: boolean
example: true
brand_id:
type: string
example: '12345'
error:
type: string
example: null
422:
description: 'Validation error'
content:
application/json:
schema:
type: object
example:
message: 'The organisation name field is required.'
errors:
organisation_name:
- 'The organisation name field is required.'
properties:
message:
type: string
example: 'The organisation name field is required.'
errors:
type: object
properties:
organisation_name:
type: array
example:
- 'The organisation name field is required.'
items:
type: string
tags:
- Onboarding
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
organisation_name:
type: string
description: 'The organisation/company name.'
example: 'Acme Inc'
product_name:
type: string
description: 'The product name.'
example: 'Acme CRM'
product_website:
type: string
description: 'The product website URL.'
example: 'https://acme.com'
product_logo:
type: string
description: 'The product logo URL.'
example: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID'
review_platforms:
type: object
description: 'Review platform settings with enabled status and URLs.'
example:
g2:
enabled: true
url: 'https://g2.com/products/acme'
capterra:
enabled: false
url: null
properties: { }
reddit_keywords:
type: array
description: 'Reddit keyword variations to track.'
example:
- acme
- 'acme crm'
items:
type: string
reddit_brand_name:
type: string
description: 'The brand name for Reddit tracking.'
example: Acme
required:
- organisation_name
- product_name
put:
summary: 'Update Onboarding'
operationId: updateOnboarding
description: 'Update the current organisation and project for the authenticated user.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Onboarding updated successfully.'
data:
organisation:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Inc'
website: 'https://acme.com'
project:
uuid: 660e8400-e29b-41d4-a716-446655440001
product_name: 'Acme CRM'
product_website: 'https://acme.com'
product_logo: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID'
review_platforms: { }
reddit_keywords:
- acme
- 'acme crm'
reddit_brand_name: Acme
scraper_sync:
success: true
synced_platforms:
- g2
- capterra
errors: []
brand_sync:
success: true
brand_id: '12345'
error: null
properties:
message:
type: string
example: 'Onboarding updated successfully.'
data:
type: object
properties:
organisation:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Inc'
website:
type: string
example: 'https://acme.com'
project:
type: object
properties:
uuid:
type: string
example: 660e8400-e29b-41d4-a716-446655440001
product_name:
type: string
example: 'Acme CRM'
product_website:
type: string
example: 'https://acme.com'
product_logo:
type: string
example: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID'
review_platforms:
type: object
properties: { }
reddit_keywords:
type: array
example:
- acme
- 'acme crm'
items:
type: string
reddit_brand_name:
type: string
example: Acme
scraper_sync:
type: object
properties:
success:
type: boolean
example: true
synced_platforms:
type: array
example:
- g2
- capterra
items:
type: string
errors:
type: array
example: []
brand_sync:
type: object
properties:
success:
type: boolean
example: true
brand_id:
type: string
example: '12345'
error:
type: string
example: null
404:
description: 'No organisation or project found'
content:
application/json:
schema:
type: object
example:
message: 'No organisation or project found for the current user.'
properties:
message:
type: string
example: 'No organisation or project found for the current user.'
422:
description: 'Validation error'
content:
application/json:
schema:
type: object
example:
message: 'The organisation name field is required.'
errors:
organisation_name:
- 'The organisation name field is required.'
properties:
message:
type: string
example: 'The organisation name field is required.'
errors:
type: object
properties:
organisation_name:
type: array
example:
- 'The organisation name field is required.'
items:
type: string
tags:
- Onboarding
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
organisation_name:
type: string
description: 'The organisation/company name.'
example: 'Acme Inc'
product_name:
type: string
description: 'The product name.'
example: 'Acme CRM'
product_website:
type: string
description: 'The product website URL.'
example: 'https://acme.com'
product_logo:
type: string
description: 'The product logo URL.'
example: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID'
review_platforms:
type: object
description: 'Review platform settings with enabled status and URLs.'
example:
g2:
enabled: true
url: 'https://g2.com/products/acme'
capterra:
enabled: false
url: null
properties: { }
reddit_keywords:
type: array
description: 'Reddit keyword variations to track.'
example:
- acme
- 'acme crm'
items:
type: string
reddit_brand_name:
type: string
description: 'The brand name for Reddit tracking.'
example: Acme
required:
- organisation_name
- product_name
/api/platform-urls:
post:
summary: 'Search Platform URLs'
operationId: searchPlatformURLs
description: "Search for a product's listing pages across multiple review platforms using Perplexity AI-powered web search.\nEach URL is validated and assigned a status: valid, invalid, or needs_review."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'All platforms'
type: object
example:
product_name: Slack
product_website: 'https://slack.com'
reviews:
g2:
url: 'https://www.g2.com/products/slack/reviews'
status: valid
capterra:
url: 'https://www.capterra.com/p/135003/Slack/'
status: valid
software_advice:
url: 'https://www.softwareadvice.com/team-communication/slack-profile/'
status: needs_review
trustpilot:
url: 'https://www.trustpilot.com/review/slack.com'
status: valid
omr_reviews:
url: 'https://omr.com/reviews/product/slack'
status: valid
clutch:
url: null
status: invalid
sourceforge:
url: 'https://sourceforge.net/software/product/Slack/'
status: valid
product_hunt:
url: 'https://www.producthunt.com/products/slack'
status: valid
hubspot_directory:
url: null
status: invalid
goodfirms:
url: 'https://www.goodfirms.co/software/slack'
status: valid
chrome_web_store:
url: null
status: invalid
google_workspace_marketplace:
url: 'https://workspace.google.com/marketplace/app/slack/429783454934'
status: needs_review
app_store:
url: null
status: invalid
play_store:
url: null
status: invalid
subscribed_fyi:
url: 'https://subscribed.fyi/slack/reviews/'
status: valid
properties:
product_name:
type: string
example: Slack
product_website:
type: string
example: 'https://slack.com'
reviews:
type: object
properties:
g2:
type: object
properties:
url:
type: string
example: 'https://www.g2.com/products/slack/reviews'
status:
type: string
example: valid
capterra:
type: object
properties:
url:
type: string
example: 'https://www.capterra.com/p/135003/Slack/'
status:
type: string
example: valid
software_advice:
type: object
properties:
url:
type: string
example: 'https://www.softwareadvice.com/team-communication/slack-profile/'
status:
type: string
example: needs_review
trustpilot:
type: object
properties:
url:
type: string
example: 'https://www.trustpilot.com/review/slack.com'
status:
type: string
example: valid
omr_reviews:
type: object
properties:
url:
type: string
example: 'https://omr.com/reviews/product/slack'
status:
type: string
example: valid
clutch:
type: object
properties:
url:
type: string
example: null
status:
type: string
example: invalid
sourceforge:
type: object
properties:
url:
type: string
example: 'https://sourceforge.net/software/product/Slack/'
status:
type: string
example: valid
product_hunt:
type: object
properties:
url:
type: string
example: 'https://www.producthunt.com/products/slack'
status:
type: string
example: valid
hubspot_directory:
type: object
properties:
url:
type: string
example: null
status:
type: string
example: invalid
goodfirms:
type: object
properties:
url:
type: string
example: 'https://www.goodfirms.co/software/slack'
status:
type: string
example: valid
chrome_web_store:
type: object
properties:
url:
type: string
example: null
status:
type: string
example: invalid
google_workspace_marketplace:
type: object
properties:
url:
type: string
example: 'https://workspace.google.com/marketplace/app/slack/429783454934'
status:
type: string
example: needs_review
app_store:
type: object
properties:
url:
type: string
example: null
status:
type: string
example: invalid
play_store:
type: object
properties:
url:
type: string
example: null
status:
type: string
example: invalid
subscribed_fyi:
type: object
properties:
url:
type: string
example: 'https://subscribed.fyi/slack/reviews/'
status:
type: string
example: valid
-
description: 'Filtered by platform'
type: object
example:
product_name: Slack
product_website: 'https://slack.com'
reviews:
g2:
url: 'https://www.g2.com/products/slack/reviews'
status: valid
properties:
product_name:
type: string
example: Slack
product_website:
type: string
example: 'https://slack.com'
reviews:
type: object
properties:
g2:
type: object
properties:
url:
type: string
example: 'https://www.g2.com/products/slack/reviews'
status:
type: string
example: valid
422:
description: 'Validation error'
content:
application/json:
schema:
type: object
example:
message: 'The product name field is required.'
errors:
product_name:
- 'The product name field is required.'
properties:
message:
type: string
example: 'The product name field is required.'
errors:
type: object
properties:
product_name:
type: array
example:
- 'The product name field is required.'
items:
type: string
tags:
- Onboarding
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
product_name:
type: string
description: 'The name of the product to search for.'
example: Slack
product_website:
type: string
description: "The product's website URL."
example: 'https://slack.com'
platform:
type: string
description: "Optional platform filter. If provided, only returns that platform's URL. Valid values: g2, capterra, software_advice, trustpilot, omr_reviews, clutch, sourceforge, product_hunt, hubspot_directory, goodfirms, chrome_web_store, google_workspace_marketplace, play_store, app_store, subscribed_fyi."
example: g2
nullable: true
required:
- product_name
- product_website
/api/reddit-keywords:
post:
summary: 'Generate Reddit Keywords'
operationId: generateRedditKeywords
description: 'Generate commonly used variations of a brand name for Reddit tracking, including misspellings, abbreviations, and nicknames.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
brand_name: Salesforce
top_variations:
exact: Salesforce
misspellings:
- Salesfoce
- Salseforce
- Saleforce
abbreviations:
- SF
- SFDC
nicknames:
- 'The Force'
- 'SF CRM'
- 'Sales Cloud'
properties:
brand_name:
type: string
example: Salesforce
top_variations:
type: object
properties:
exact:
type: string
example: Salesforce
misspellings:
type: array
example:
- Salesfoce
- Salseforce
- Saleforce
items:
type: string
abbreviations:
type: array
example:
- SF
- SFDC
items:
type: string
nicknames:
type: array
example:
- 'The Force'
- 'SF CRM'
- 'Sales Cloud'
items:
type: string
422:
description: 'Validation error'
content:
application/json:
schema:
type: object
example:
message: 'The brand name field is required.'
errors:
brand_name:
- 'The brand name field is required.'
properties:
message:
type: string
example: 'The brand name field is required.'
errors:
type: object
properties:
brand_name:
type: array
example:
- 'The brand name field is required.'
items:
type: string
tags:
- Onboarding
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
brand_name:
type: string
description: 'The brand name to generate variations for.'
example: Salesforce
required:
- brand_name
/api/organisations:
get:
summary: 'List Organisations'
operationId: listOrganisations
description: 'Get all organisations the authenticated user belongs to.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Inc'
website: 'https://acme.com'
role: organisation_owner
created_at: '2025-12-10T10:00:00.000000Z'
updated_at: '2025-12-10T10:00:00.000000Z'
properties:
data:
type: array
example:
-
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Inc'
website: 'https://acme.com'
role: organisation_owner
created_at: '2025-12-10T10:00:00.000000Z'
updated_at: '2025-12-10T10:00:00.000000Z'
items:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Inc'
website:
type: string
example: 'https://acme.com'
role:
type: string
example: organisation_owner
created_at:
type: string
example: '2025-12-10T10:00:00.000000Z'
updated_at:
type: string
example: '2025-12-10T10:00:00.000000Z'
tags:
- Organisations
post:
summary: 'Create Organisation'
operationId: createOrganisation
description: 'Create a new organisation. The authenticated user becomes the admin.'
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Organisation created successfully.'
data:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Inc'
website: 'https://acme.com'
role: organisation_owner
created_at: '2025-12-10T10:00:00.000000Z'
updated_at: '2025-12-10T10:00:00.000000Z'
properties:
message:
type: string
example: 'Organisation created successfully.'
data:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Inc'
website:
type: string
example: 'https://acme.com'
role:
type: string
example: organisation_owner
created_at:
type: string
example: '2025-12-10T10:00:00.000000Z'
updated_at:
type: string
example: '2025-12-10T10:00:00.000000Z'
tags:
- Organisations
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The organisation name.'
example: 'Acme Inc'
website:
type: string
description: 'optional The organisation website URL.'
example: 'https://acme.com'
nullable: true
required:
- name
'/api/organisations/{uuid}':
get:
summary: 'Get Organisation'
operationId: getOrganisation
description: 'Get details of a specific organisation.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Inc'
website: 'https://acme.com'
role: organisation_owner
created_at: '2025-12-10T10:00:00.000000Z'
updated_at: '2025-12-10T10:00:00.000000Z'
properties:
data:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Inc'
website:
type: string
example: 'https://acme.com'
role:
type: string
example: organisation_owner
created_at:
type: string
example: '2025-12-10T10:00:00.000000Z'
updated_at:
type: string
example: '2025-12-10T10:00:00.000000Z'
403:
description: 'No access'
content:
application/json:
schema:
type: object
example:
message: 'You do not have access to this organisation.'
properties:
message:
type: string
example: 'You do not have access to this organisation.'
tags:
- Organisations
put:
summary: 'Update Organisation'
operationId: updateOrganisation
description: "Update an organisation's details. Requires admin role."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Organisation updated successfully.'
data:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Corp'
website: 'https://acme.com'
role: organisation_owner
created_at: '2025-12-10T10:00:00.000000Z'
updated_at: '2025-12-10T10:00:00.000000Z'
properties:
message:
type: string
example: 'Organisation updated successfully.'
data:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Corp'
website:
type: string
example: 'https://acme.com'
role:
type: string
example: organisation_owner
created_at:
type: string
example: '2025-12-10T10:00:00.000000Z'
updated_at:
type: string
example: '2025-12-10T10:00:00.000000Z'
403:
description: 'Not admin'
content:
application/json:
schema:
type: object
example:
message: 'You must be an organisation admin to perform this action.'
properties:
message:
type: string
example: 'You must be an organisation admin to perform this action.'
tags:
- Organisations
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The organisation name.'
example: 'Acme Corp'
website:
type: string
description: 'optional The organisation website URL.'
example: 'https://acme.com'
nullable: true
required:
- name
parameters:
-
in: path
name: uuid
description: ''
example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c
required: true
schema:
type: string
-
in: path
name: organisation
description: 'The organisation UUID.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
'/api/organisations/{organisation_uuid}/switch':
post:
summary: 'Switch Current Organisation'
operationId: switchCurrentOrganisation
description: "Set the user's current organisation for subsequent requests."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Switched to organisation successfully.'
data:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Inc'
properties:
message:
type: string
example: 'Switched to organisation successfully.'
data:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Inc'
403:
description: 'No access'
content:
application/json:
schema:
type: object
example:
message: 'You do not have access to this organisation.'
properties:
message:
type: string
example: 'You do not have access to this organisation.'
tags:
- Organisations
parameters:
-
in: path
name: organisation_uuid
description: ''
example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c
required: true
schema:
type: string
-
in: path
name: organisation
description: 'The organisation UUID.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
/api/platforms:
get:
summary: 'List All Platforms'
operationId: listAllPlatforms
description: 'Get all platforms.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: array
items:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: G2
created_at:
type: string
example: '2026-01-08T10:00:00.000000Z'
updated_at:
type: string
example: '2026-01-08T10:00:00.000000Z'
example:
-
uuid: 550e8400-e29b-41d4-a716-446655440000
name: G2
created_at: '2026-01-08T10:00:00.000000Z'
updated_at: '2026-01-08T10:00:00.000000Z'
tags:
- Platforms
post:
summary: 'Create Platform'
operationId: createPlatform
description: 'Create a new platform.'
parameters: []
responses:
201:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Platform created successfully.'
data:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: G2
created_at: '2026-01-08T10:00:00.000000Z'
updated_at: '2026-01-08T10:00:00.000000Z'
properties:
message:
type: string
example: 'Platform created successfully.'
data:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: G2
created_at:
type: string
example: '2026-01-08T10:00:00.000000Z'
updated_at:
type: string
example: '2026-01-08T10:00:00.000000Z'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The name has already been taken.'
errors:
name:
- 'The name has already been taken.'
properties:
message:
type: string
example: 'The name has already been taken.'
errors:
type: object
properties:
name:
type: array
example:
- 'The name has already been taken.'
items:
type: string
tags:
- Platforms
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The platform name.'
example: G2
required:
- name
'/api/platforms/{uuid}':
patch:
summary: 'Update Platform'
operationId: updatePlatform
description: "Update a platform's details."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Platform updated successfully.'
data:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: Capterra
created_at: '2026-01-08T10:00:00.000000Z'
updated_at: '2026-01-08T10:30:00.000000Z'
properties:
message:
type: string
example: 'Platform updated successfully.'
data:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: Capterra
created_at:
type: string
example: '2026-01-08T10:00:00.000000Z'
updated_at:
type: string
example: '2026-01-08T10:30:00.000000Z'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Platform not found.'
properties:
message:
type: string
example: 'Platform not found.'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The name has already been taken.'
errors:
name:
- 'The name has already been taken.'
properties:
message:
type: string
example: 'The name has already been taken.'
errors:
type: object
properties:
name:
type: array
example:
- 'The name has already been taken.'
items:
type: string
tags:
- Platforms
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The platform name.'
example: Capterra
required:
- name
delete:
summary: 'Delete Platform'
operationId: deletePlatform
description: 'Delete a platform.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
message: 'Platform deleted successfully.'
properties:
message:
type: string
example: 'Platform deleted successfully.'
404:
description: 'Not Found'
content:
application/json:
schema:
type: object
example:
message: 'Platform not found.'
properties:
message:
type: string
example: 'Platform not found.'
tags:
- Platforms
parameters:
-
in: path
name: uuid
description: 'The platform UUID.'
example: 550e8400-e29b-41d4-a716-446655440000
required: true
schema:
type: string
/api/products/search:
get:
summary: 'Search Products'
operationId: searchProducts
description: 'Search for products in the Curiosity database to claim.'
parameters:
-
in: query
name: q
description: 'The search query.'
example: slack
required: true
schema:
type: string
description: 'The search query.'
example: slack
-
in: query
name: limit
description: 'The maximum number of results (default 10, max 50).'
example: 10
required: false
schema:
type: integer
description: 'The maximum number of results (default 10, max 50).'
example: 10
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
products:
-
id: 123
name: Slack
url: 'https://slack.com'
logo_path: products/slack-logo.png
properties:
data:
type: object
properties:
products:
type: array
example:
-
id: 123
name: Slack
url: 'https://slack.com'
logo_path: products/slack-logo.png
items:
type: object
properties:
id:
type: integer
example: 123
name:
type: string
example: Slack
url:
type: string
example: 'https://slack.com'
logo_path:
type: string
example: products/slack-logo.png
tags:
- Profiles
/api/claim-profiles:
get:
summary: 'List All Claim Profiles'
operationId: listAllClaimProfiles
description: 'Get all claim profiles across all organisations. Intended for admin panel usage.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: array
items:
type: object
properties:
uuid:
type: string
example: 880e8400-e29b-41d4-a716-446655440000
business_email:
type: string
example: john@company.com
job_title:
type: string
example: 'Product Manager'
business_phone:
type: string
example: '+1234567890'
status:
type: string
example: pending
project:
type: object
properties:
uuid:
type: string
example: 660e8400-e29b-41d4-a716-446655440001
product_name:
type: string
example: 'Acme App'
organisation:
type: object
properties:
uuid:
type: string
example: 550e8400-e29b-41d4-a716-446655440000
name:
type: string
example: 'Acme Corp'
status_updated_by:
type: string
example: null
created_at:
type: string
example: '2026-01-03T12:00:00.000000Z'
updated_at:
type: string
example: '2026-01-03T12:00:00.000000Z'
example:
-
uuid: 880e8400-e29b-41d4-a716-446655440000
business_email: john@company.com
job_title: 'Product Manager'
business_phone: '+1234567890'
status: pending
project:
uuid: 660e8400-e29b-41d4-a716-446655440001
product_name: 'Acme App'
organisation:
uuid: 550e8400-e29b-41d4-a716-446655440000
name: 'Acme Corp'
status_updated_by: null
created_at: '2026-01-03T12:00:00.000000Z'
updated_at: '2026-01-03T12:00:00.000000Z'
tags:
- Profiles
post:
summary: 'Create Claim Profile'
operationId: createClaimProfile
description: "Create a new claim profile for a project. The organisation is automatically\nderived from the authenticated user's current organisation context."
parameters: []
responses:
201:
description: Success
content:
application/json:
schema:
type: object
example:
message: 'Claim profile created successfully.'
data:
uuid: 880e8400-e29b-41d4-a716-446655440000
scraper_product_id: 123
product_name: Slack
product_url: 'https://slack.com'
business_email: john@company.com
job_title: 'Product Manager'
business_phone: '+1234567890'
status: pending
created_at: '2026-01-03T12:00:00.000000Z'
updated_at: '2026-01-03T12:00:00.000000Z'
properties:
message:
type: string
example: 'Claim profile created successfully.'
data:
type: object
properties:
uuid:
type: string
example: 880e8400-e29b-41d4-a716-446655440000
scraper_product_id:
type: integer
example: 123
product_name:
type: string
example: Slack
product_url:
type: string
example: 'https://slack.com'
business_email:
type: string
example: john@company.com
job_title:
type: string
example: 'Product Manager'
business_phone:
type: string
example: '+1234567890'
status:
type: string
example: pending
created_at:
type: string
example: '2026-01-03T12:00:00.000000Z'
updated_at:
type: string
example: '2026-01-03T12:00:00.000000Z'
403:
description: 'No organisation context'
content:
application/json:
schema:
type: object
example:
message: 'This action is unauthorized.'
properties:
message:
type: string
example: 'This action is unauthorized.'
422:
description: 'Validation Error'
content:
application/json:
schema:
type: object
example:
message: 'The project is required. (and 1 more error)'
errors:
project_id:
- 'The project is required.'
business_email:
- 'Please use a business email address.'
properties:
message:
type: string
example: 'The project is required. (and 1 more error)'
errors:
type: object
properties:
project_id:
type: array
example:
- 'The project is required.'
items:
type: string
business_email:
type: array
example:
- 'Please use a business email address.'
items:
type: string
tags:
- Profiles
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
project_id:
type: string
description: 'The project UUID.'
example: 660e8400-e29b-41d4-a716-446655440001
scraper_product_id:
type: integer
description: 'The ID of the product from Curiosity.'
example: 123
product_name:
type: string
description: 'The name of the product being claimed.'
example: Slack
product_url:
type: string
description: 'The URL of the product.'
example: 'https://slack.com'
nullable: true
business_email:
type: string
description: 'The business email address.'
example: john@company.com
job_title:
type: string
description: 'The job title.'
example: 'Product Manager'
business_phone:
type: string
description: 'The business phone number (optional).'
example: '+1234567890'
nullable: true
required:
- project_id
- scraper_product_id
- product_name
- business_email
- job_title
'/api/projects/{project_uuid}/profile':
get:
summary: 'Get Product Profile'
operationId: getProductProfile
description: "Get the product profile for a specific project.\nReturns all profile data including content, SEO meta, categories, and sync status."
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
uuid: 770e8400-e29b-41d4-a716-446655440002
curiosity_product_id: 123
name: Slack
url: 'https://slack.com'
logo_path: products/slack-logo.png
local_logo: null
subtitle: '
Where work happens
' overview: 'Slack is a messaging platform...
' pricing: 'Free, Pro $7.25/user, Business+ $12.50/user
' verified_badge: true parent_category: id: 5 name: Communication review_platforms: g2: enabled: true url: 'https://g2.com/products/slack' score: 4.5 reviews_count: 120 capterra: enabled: true url: 'https://capterra.com/p/123/slack' score: 4.6 reviews_count: 85 trustpilot: score: 3.6 reviews_count: 11218 categories: - id: 1 name: Communication segments: - id: 1 name: Enterprise search_fields: built_for: - id: 1 name: 'Marketing Teams' platform: - id: 2 name: Web pricing_model: - id: 3 name: Subscription competitors: - id: 456 name: 'Microsoft Teams' url: 'https://teams.microsoft.com' logo_path: null videos: - 'https://www.youtube.com/watch?v=abc' awards: - id: 1 name: 'Best Communication Tool 2025' deal: 'Get 20% off annual plans' deals_meta_title: 'Best Slack Deals' deals_meta_description: null cancellation_content: 'To cancel your subscription...
' cancellation_content_summary: 'Cancel anytime from settings
' book_demo_url: 'https://slack.com/demo' pricing_url: 'https://slack.com/pricing' pros_cons: "Pros: Easy to use\nCons: Can be expensive" analysis: 'Detailed analysis of Slack...
' faq: "Q: How much does it cost?\nA: Free tier available" alternatives_text: 'Consider Microsoft Teams or Discord...
' pricing_range: $0-$15/user/mo is_ai_powered: false meta: main_page: title: 'Slack - Where Work Happens' description: 'Team messaging platform' deals: title: 'Slack Deals' description: 'Best Slack discounts' cancellation: title: 'Cancel Slack' description: 'How to cancel' sync_status: synced synced_at: '2026-01-06T10:00:00.000000Z' local_changes_at: null last_change_request: uuid: 880e8400-e29b-41d4-a716-446655440003 status: pending requested_at: '2026-01-06T11:00:00.000000Z' reviewed_at: null created_at: '2026-01-06T09:00:00.000000Z' updated_at: '2026-01-06T10:00:00.000000Z' properties: data: type: object properties: uuid: type: string example: 770e8400-e29b-41d4-a716-446655440002 curiosity_product_id: type: integer example: 123 name: type: string example: Slack url: type: string example: 'https://slack.com' logo_path: type: string example: products/slack-logo.png local_logo: type: string example: null subtitle: type: string example: 'Where work happens
' overview: type: string example: 'Slack is a messaging platform...
' pricing: type: string example: 'Free, Pro $7.25/user, Business+ $12.50/user
' verified_badge: type: boolean example: true parent_category: type: object properties: id: type: integer example: 5 name: type: string example: Communication review_platforms: type: object properties: g2: type: object properties: enabled: type: boolean example: true url: type: string example: 'https://g2.com/products/slack' score: type: number example: 4.5 reviews_count: type: integer example: 120 capterra: type: object properties: enabled: type: boolean example: true url: type: string example: 'https://capterra.com/p/123/slack' score: type: number example: 4.6 reviews_count: type: integer example: 85 trustpilot: type: object properties: score: type: number example: 3.6 reviews_count: type: integer example: 11218 categories: type: array example: - id: 1 name: Communication items: type: object properties: id: type: integer example: 1 name: type: string example: Communication segments: type: array example: - id: 1 name: Enterprise items: type: object properties: id: type: integer example: 1 name: type: string example: Enterprise search_fields: type: object properties: built_for: type: array example: - id: 1 name: 'Marketing Teams' items: type: object properties: id: type: integer example: 1 name: type: string example: 'Marketing Teams' platform: type: array example: - id: 2 name: Web items: type: object properties: id: type: integer example: 2 name: type: string example: Web pricing_model: type: array example: - id: 3 name: Subscription items: type: object properties: id: type: integer example: 3 name: type: string example: Subscription competitors: type: array example: - id: 456 name: 'Microsoft Teams' url: 'https://teams.microsoft.com' logo_path: null items: type: object properties: id: type: integer example: 456 name: type: string example: 'Microsoft Teams' url: type: string example: 'https://teams.microsoft.com' logo_path: type: string example: null videos: type: array example: - 'https://www.youtube.com/watch?v=abc' items: type: string awards: type: array example: - id: 1 name: 'Best Communication Tool 2025' items: type: object properties: id: type: integer example: 1 name: type: string example: 'Best Communication Tool 2025' deal: type: string example: 'Get 20% off annual plans' deals_meta_title: type: string example: 'Best Slack Deals' deals_meta_description: type: string example: null cancellation_content: type: string example: 'To cancel your subscription...
' cancellation_content_summary: type: string example: 'Cancel anytime from settings
' book_demo_url: type: string example: 'https://slack.com/demo' pricing_url: type: string example: 'https://slack.com/pricing' pros_cons: type: string example: "Pros: Easy to use\nCons: Can be expensive" analysis: type: string example: 'Detailed analysis of Slack...
' faq: type: string example: "Q: How much does it cost?\nA: Free tier available" alternatives_text: type: string example: 'Consider Microsoft Teams or Discord...
' pricing_range: type: string example: $0-$15/user/mo is_ai_powered: type: boolean example: false meta: type: object properties: main_page: type: object properties: title: type: string example: 'Slack - Where Work Happens' description: type: string example: 'Team messaging platform' deals: type: object properties: title: type: string example: 'Slack Deals' description: type: string example: 'Best Slack discounts' cancellation: type: object properties: title: type: string example: 'Cancel Slack' description: type: string example: 'How to cancel' sync_status: type: string example: synced synced_at: type: string example: '2026-01-06T10:00:00.000000Z' local_changes_at: type: string example: null last_change_request: type: object properties: uuid: type: string example: 880e8400-e29b-41d4-a716-446655440003 status: type: string example: pending requested_at: type: string example: '2026-01-06T11:00:00.000000Z' reviewed_at: type: string example: null created_at: type: string example: '2026-01-06T09:00:00.000000Z' updated_at: type: string example: '2026-01-06T10:00:00.000000Z' 404: description: '' content: application/json: schema: oneOf: - description: '' type: object example: message: 'Project not found.' properties: message: type: string example: 'Project not found.' - description: '' type: object example: message: 'This project does not have a product profile yet.' properties: message: type: string example: 'This project does not have a product profile yet.' tags: - Profiles put: summary: 'Update Product Profile' operationId: updateProductProfile description: "Submit a change request for the product profile. Changes are stored as a pending\nrequest for admin review. Once approved, changes will be applied and synced to Curiosity.\nFields like subtitle, overview, pricing, analysis, and alternatives_text support HTML/richtext." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: message: 'Change request submitted for review.' data: change_request_uuid: 880e8400-e29b-41d4-a716-446655440003 status: pending properties: message: type: string example: 'Change request submitted for review.' data: type: object properties: change_request_uuid: type: string example: 880e8400-e29b-41d4-a716-446655440003 status: type: string example: pending 404: description: '' content: application/json: schema: type: object example: message: 'This project does not have a product profile yet.' properties: message: type: string example: 'This project does not have a product profile yet.' tags: - Profiles requestBody: required: false content: application/json: schema: type: object properties: subtitle: type: string description: 'The product tagline (HTML supported).' example: 'Where work happens
' overview: type: string description: 'The product description (HTML supported).' example: 'Slack is a messaging platform...
' pricing: type: string description: 'Pricing information (HTML supported).' example: 'Free, Pro $7.25/user
' parent_category_id: type: integer description: 'The parent category ID from Curiosity.' example: 5 review_platforms: type: object description: 'Review platform URLs and data.' example: [] properties: g2: type: object description: '' example: url: 'https://g2.com/products/slack' properties: url: type: string description: 'G2 review page URL.' example: 'https://g2.com/products/slack' enabled: type: boolean description: 'Whether G2 is enabled.' example: true capterra: type: object description: '' example: url: 'https://capterra.com/p/123/slack' properties: url: type: string description: 'Capterra review page URL.' example: 'https://capterra.com/p/123/slack' enabled: type: boolean description: 'Whether Capterra is enabled.' example: true videos: type: array description: 'List of video URLs.' example: - architecto items: type: string categories: type: array description: 'List of category objects with id and name.' example: - [] items: type: object segments: type: array description: 'List of segment objects with id and name.' example: - [] items: type: object search_fields: type: object description: 'Search field categorization by type (built_for, platform, pricing_model arrays).' example: [] properties: { } competitors: type: array description: 'List of competitor products with id, name, url, logo_path.' example: - [] items: type: object deal: type: string description: 'Deal content/description.' example: architecto deals_meta_title: type: string description: 'SEO meta title for deals page.' example: 'Best Slack Deals 2026' deals_meta_description: type: string description: 'SEO meta description for deals page.' example: architecto cancellation_content: type: string description: 'How to cancel subscription content (HTML supported).' example: architecto cancellation_content_summary: type: string description: 'Summary of cancellation info (HTML supported).' example: architecto book_demo_url: type: string description: 'URL to book a demo.' example: 'https://slack.com/demo' pricing_url: type: string description: 'URL to pricing page.' example: 'https://slack.com/pricing' pros_cons: type: string description: 'Product pros and cons.' example: architecto analysis: type: string description: 'Detailed product analysis (HTML supported).' example: architecto faq: type: string description: 'Frequently asked questions.' example: architecto alternatives_text: type: string description: 'Description of alternatives (HTML supported).' example: architecto pricing_range: type: string description: 'Price tier display.' example: $99-$999/mo is_ai_powered: type: boolean description: 'Whether product uses AI technology.' example: true meta: type: object description: 'SEO meta tags for various pages.' example: [] properties: main_page: type: object description: '' example: title: 'Slack - Where Work Happens' properties: title: type: string description: 'Main page meta title.' example: 'Slack - Where Work Happens' description: type: string description: 'Main page meta description.' example: 'Eius et animi quos velit et.' deals: type: object description: '' example: title: architecto properties: title: type: string description: 'Deals page meta title.' example: architecto description: type: string description: 'Deals page meta description.' example: 'Eius et animi quos velit et.' cancellation: type: object description: '' example: title: architecto properties: title: type: string description: 'Cancellation page meta title.' example: architecto description: type: string description: 'Cancellation page meta description.' example: 'Eius et animi quos velit et.' parameters: - in: path name: project_uuid description: 'The project UUID.' example: 660e8400-e29b-41d4-a716-446655440001 required: true schema: type: string '/api/projects/{project_uuid}/profile/logo': post: summary: 'Upload Product Logo' operationId: uploadProductLogo description: "Upload a new logo image for the product profile. The logo is stored locally\nand will be synced to Curiosity when you push changes." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: message: 'Logo uploaded successfully.' data: local_logo: product-logos/abc123.png sync_status: local_changes local_changes_at: '2026-01-06T11:00:00.000000Z' properties: message: type: string example: 'Logo uploaded successfully.' data: type: object properties: local_logo: type: string example: product-logos/abc123.png sync_status: type: string example: local_changes local_changes_at: type: string example: '2026-01-06T11:00:00.000000Z' 404: description: '' content: application/json: schema: type: object example: message: 'This project does not have a product profile yet.' properties: message: type: string example: 'This project does not have a product profile yet.' 422: description: '' content: application/json: schema: type: object example: message: 'The logo field is required.' errors: logo: - 'The logo field is required.' properties: message: type: string example: 'The logo field is required.' errors: type: object properties: logo: type: array example: - 'The logo field is required.' items: type: string tags: - Profiles requestBody: required: true content: multipart/form-data: schema: type: object properties: logo: type: string format: binary description: 'The logo image file (max 5MB, must be an image).' required: - logo parameters: - in: path name: project_uuid description: 'The project UUID.' example: 660e8400-e29b-41d4-a716-446655440001 required: true schema: type: string '/api/projects/{project_uuid}/profile/status': get: summary: 'Get Sync Status' operationId: getSyncStatus description: "Get the current sync status of a project's product profile." parameters: [] responses: 200: description: '' content: application/json: schema: oneOf: - description: '' type: object example: data: has_profile: true has_curiosity_link: true sync_status: local_changes synced_at: '2026-01-06T10:00:00.000000Z' local_changes_at: '2026-01-06T11:00:00.000000Z' last_change_request: uuid: 880e8400-e29b-41d4-a716-446655440003 status: pending requested_at: '2026-01-06T11:00:00.000000Z' reviewed_at: null properties: data: type: object properties: has_profile: type: boolean example: true has_curiosity_link: type: boolean example: true sync_status: type: string example: local_changes synced_at: type: string example: '2026-01-06T10:00:00.000000Z' local_changes_at: type: string example: '2026-01-06T11:00:00.000000Z' last_change_request: type: object properties: uuid: type: string example: 880e8400-e29b-41d4-a716-446655440003 status: type: string example: pending requested_at: type: string example: '2026-01-06T11:00:00.000000Z' reviewed_at: type: string example: null - description: '' type: object example: data: has_profile: false has_curiosity_link: true sync_status: null synced_at: null local_changes_at: null last_change_request: null properties: data: type: object properties: has_profile: type: boolean example: false has_curiosity_link: type: boolean example: true sync_status: type: string example: null synced_at: type: string example: null local_changes_at: type: string example: null last_change_request: type: string example: null tags: - Profiles parameters: - in: path name: project_uuid description: 'The project UUID.' example: 660e8400-e29b-41d4-a716-446655440001 required: true schema: type: string /api/catalog/parent-categories: get: summary: 'List Parent Categories' operationId: listParentCategories description: "Get all top-level parent categories for product classification.\nParent categories represent broad product domains." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 name: Analytics - id: 2 name: Communication - id: 3 name: Marketing - id: 4 name: 'Project Management' - id: 5 name: Sales properties: data: type: array example: - id: 1 name: Analytics - id: 2 name: Communication - id: 3 name: Marketing - id: 4 name: 'Project Management' - id: 5 name: Sales items: type: object properties: id: type: integer example: 1 name: type: string example: Analytics tags: - Profiles /api/catalog/categories: get: summary: 'List Categories' operationId: listCategories description: "Get all detailed categories for product classification.\nCategories are more specific than parent categories and can be assigned to products." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 name: 'Video Conferencing' - id: 2 name: 'Team Chat' - id: 3 name: 'Email Marketing' - id: 4 name: CRM - id: 5 name: 'Task Management' properties: data: type: array example: - id: 1 name: 'Video Conferencing' - id: 2 name: 'Team Chat' - id: 3 name: 'Email Marketing' - id: 4 name: CRM - id: 5 name: 'Task Management' items: type: object properties: id: type: integer example: 1 name: type: string example: 'Video Conferencing' tags: - Profiles /api/catalog/segments: get: summary: 'List Segments' operationId: listSegments description: "Get all market segments for product targeting.\nSegments define the target audience or market size for products." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 name: Enterprise - id: 2 name: Mid-Market - id: 3 name: SMB - id: 4 name: Startup - id: 5 name: Freelancer properties: data: type: array example: - id: 1 name: Enterprise - id: 2 name: Mid-Market - id: 3 name: SMB - id: 4 name: Startup - id: 5 name: Freelancer items: type: object properties: id: type: integer example: 1 name: type: string example: Enterprise tags: - Profiles /api/catalog/search-fields: get: summary: 'List Search Fields' operationId: listSearchFields description: "Get all search field options grouped by type.\nSearch fields are structured attributes used for filtering and discovery.\n\n**Field Types:**\n- `built_for` - Target user roles or teams (e.g., \"Marketing Teams\", \"Developers\")\n- `platform` - Deployment platforms (e.g., \"Web\", \"iOS\", \"Android\", \"Desktop\")\n- `pricing_model` - Business models (e.g., \"Subscription\", \"One-time\", \"Freemium\")" parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: built_for: - id: 1 name: 'Marketing Teams' - id: 2 name: 'Sales Teams' - id: 3 name: Developers - id: 4 name: 'HR Teams' platform: - id: 1 name: Web - id: 2 name: iOS - id: 3 name: Android - id: 4 name: Desktop pricing_model: - id: 1 name: Subscription - id: 2 name: 'One-time Purchase' - id: 3 name: Freemium - id: 4 name: Usage-based properties: data: type: object properties: built_for: type: array example: - id: 1 name: 'Marketing Teams' - id: 2 name: 'Sales Teams' - id: 3 name: Developers - id: 4 name: 'HR Teams' items: type: object properties: id: type: integer example: 1 name: type: string example: 'Marketing Teams' platform: type: array example: - id: 1 name: Web - id: 2 name: iOS - id: 3 name: Android - id: 4 name: Desktop items: type: object properties: id: type: integer example: 1 name: type: string example: Web pricing_model: type: array example: - id: 1 name: Subscription - id: 2 name: 'One-time Purchase' - id: 3 name: Freemium - id: 4 name: Usage-based items: type: object properties: id: type: integer example: 1 name: type: string example: Subscription tags: - Profiles /api/external/claim-profiles: post: summary: 'Create Claim Profile (External)' operationId: createClaimProfileExternal description: "Create a new claim profile from an external website. This endpoint does not require\nuser authentication but requires a valid external API key." parameters: [] responses: 201: description: Success content: application/json: schema: type: object example: message: 'Claim profile submitted successfully.' data: uuid: 880e8400-e29b-41d4-a716-446655440000 product_name: Lovable business_email: john@company.com status: pending properties: message: type: string example: 'Claim profile submitted successfully.' data: type: object properties: uuid: type: string example: 880e8400-e29b-41d4-a716-446655440000 product_name: type: string example: Lovable business_email: type: string example: john@company.com status: type: string example: pending 401: description: Unauthorized content: application/json: schema: type: object example: message: Unauthorized. properties: message: type: string example: Unauthorized. 422: description: 'Validation Error' content: application/json: schema: type: object example: message: 'The business email is required.' errors: business_email: - 'The business email is required.' properties: message: type: string example: 'The business email is required.' errors: type: object properties: business_email: type: array example: - 'The business email is required.' items: type: string tags: - Profiles requestBody: required: true content: application/json: schema: type: object properties: scraper_product_id: type: integer description: 'The ID of the product from Curiosity.' example: 123 product_name: type: string description: 'The name of the product being claimed.' example: Lovable product_url: type: string description: 'The URL of the product.' example: 'https://lovable.dev' nullable: true business_email: type: string description: 'The business email address.' example: john@company.com job_title: type: string description: 'The job title.' example: 'Product Manager' nullable: true business_phone: type: string description: 'The business phone number (optional).' example: '+1234567890' nullable: true required: - scraper_product_id - product_name - business_email security: [] '/api/organisations/{organisation_uuid}/projects': get: summary: 'List Projects' operationId: listProjects description: 'Get all projects in an organisation that the user has access to.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: - uuid: 660e8400-e29b-41d4-a716-446655440001 product_name: 'Acme App' product_website: 'https://acme.com' review_platforms: g2: enabled: true url: 'https://g2.com/products/acme' reddit_keywords: - acme - 'acme app' created_at: '2025-12-10T10:00:00.000000Z' updated_at: '2025-12-10T10:00:00.000000Z' properties: data: type: array example: - uuid: 660e8400-e29b-41d4-a716-446655440001 product_name: 'Acme App' product_website: 'https://acme.com' review_platforms: g2: enabled: true url: 'https://g2.com/products/acme' reddit_keywords: - acme - 'acme app' created_at: '2025-12-10T10:00:00.000000Z' updated_at: '2025-12-10T10:00:00.000000Z' items: type: object properties: uuid: type: string example: 660e8400-e29b-41d4-a716-446655440001 product_name: type: string example: 'Acme App' product_website: type: string example: 'https://acme.com' review_platforms: type: object properties: g2: type: object properties: enabled: type: boolean example: true url: type: string example: 'https://g2.com/products/acme' reddit_keywords: type: array example: - acme - 'acme app' items: type: string created_at: type: string example: '2025-12-10T10:00:00.000000Z' updated_at: type: string example: '2025-12-10T10:00:00.000000Z' tags: - Projects post: summary: 'Create Project' operationId: createProject description: 'Create a new project in an organisation. Requires organisation admin role.' parameters: [] responses: 201: description: '' content: application/json: schema: type: object example: message: 'Project created successfully.' data: uuid: 660e8400-e29b-41d4-a716-446655440001 product_name: 'Acme App' product_website: 'https://acme.com' product_logo: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID' review_platforms: { } reddit_keywords: - acme reddit_brand_name: Acme created_at: '2025-12-10T10:00:00.000000Z' updated_at: '2025-12-10T10:00:00.000000Z' properties: message: type: string example: 'Project created successfully.' data: type: object properties: uuid: type: string example: 660e8400-e29b-41d4-a716-446655440001 product_name: type: string example: 'Acme App' product_website: type: string example: 'https://acme.com' product_logo: type: string example: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID' review_platforms: type: object properties: { } reddit_keywords: type: array example: - acme items: type: string reddit_brand_name: type: string example: Acme created_at: type: string example: '2025-12-10T10:00:00.000000Z' updated_at: type: string example: '2025-12-10T10:00:00.000000Z' tags: - Projects requestBody: required: true content: application/json: schema: type: object properties: product_name: type: string description: 'The product name.' example: 'Acme App' product_website: type: string description: 'The product website URL.' example: 'https://acme.com' product_logo: type: string description: 'The product logo URL.' example: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID' review_platforms: type: object description: 'Review platform settings.' example: g2: enabled: true url: 'https://g2.com/products/acme' capterra: enabled: false url: null properties: { } reddit_keywords: type: array description: 'Reddit keyword variations.' example: - acme - 'acme app' items: type: string reddit_brand_name: type: string description: 'The brand name for Reddit tracking.' example: Acme social_platform: type: object description: 'Social platform configuration.' example: twitter: enabled: true url: 'https://twitter.com/acme' properties: { } required: - product_name parameters: - in: path name: organisation_uuid description: '' example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c required: true schema: type: string - in: path name: organisation description: 'The organisation UUID.' example: 550e8400-e29b-41d4-a716-446655440000 required: true schema: type: string '/api/organisations/{organisation_uuid}/projects/{uuid}': get: summary: 'Get Project' operationId: getProject description: 'Get details of a specific project.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: uuid: 660e8400-e29b-41d4-a716-446655440001 product_name: 'Acme App' product_website: 'https://acme.com' review_platforms: g2: enabled: true url: 'https://g2.com/products/acme' reddit_keywords: - acme - 'acme app' created_at: '2025-12-10T10:00:00.000000Z' updated_at: '2025-12-10T10:00:00.000000Z' properties: data: type: object properties: uuid: type: string example: 660e8400-e29b-41d4-a716-446655440001 product_name: type: string example: 'Acme App' product_website: type: string example: 'https://acme.com' review_platforms: type: object properties: g2: type: object properties: enabled: type: boolean example: true url: type: string example: 'https://g2.com/products/acme' reddit_keywords: type: array example: - acme - 'acme app' items: type: string created_at: type: string example: '2025-12-10T10:00:00.000000Z' updated_at: type: string example: '2025-12-10T10:00:00.000000Z' tags: - Projects put: summary: 'Update Project' operationId: updateProject description: "Update a project's details. Requires organisation admin role." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: message: 'Project updated successfully.' data: uuid: 660e8400-e29b-41d4-a716-446655440001 product_name: 'Acme App Pro' product_website: 'https://acme.com' product_logo: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID' review_platforms: { } reddit_keywords: - acme - 'acme pro' reddit_brand_name: Acme created_at: '2025-12-10T10:00:00.000000Z' updated_at: '2025-12-10T10:00:00.000000Z' properties: message: type: string example: 'Project updated successfully.' data: type: object properties: uuid: type: string example: 660e8400-e29b-41d4-a716-446655440001 product_name: type: string example: 'Acme App Pro' product_website: type: string example: 'https://acme.com' product_logo: type: string example: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID' review_platforms: type: object properties: { } reddit_keywords: type: array example: - acme - 'acme pro' items: type: string reddit_brand_name: type: string example: Acme created_at: type: string example: '2025-12-10T10:00:00.000000Z' updated_at: type: string example: '2025-12-10T10:00:00.000000Z' tags: - Projects requestBody: required: true content: application/json: schema: type: object properties: product_name: type: string description: 'The product name.' example: 'Acme App Pro' product_website: type: string description: 'The product website URL.' example: 'https://acme.com' product_logo: type: string description: 'The product logo URL.' example: 'https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID' review_platforms: type: object description: 'Review platform settings.' example: g2: enabled: true url: 'https://g2.com/products/acme' capterra: enabled: false url: null properties: { } reddit_keywords: type: array description: 'Reddit keyword variations.' example: - acme - 'acme pro' items: type: string reddit_brand_name: type: string description: 'The brand name for Reddit tracking.' example: Acme social_platform: type: object description: 'Social platform configuration.' example: twitter: enabled: true url: 'https://twitter.com/acme' properties: { } required: - product_name delete: summary: 'Delete Project' operationId: deleteProject description: 'Delete a project. Requires organisation admin role.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: message: 'Project deleted successfully.' properties: message: type: string example: 'Project deleted successfully.' tags: - Projects parameters: - in: path name: organisation_uuid description: '' example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c required: true schema: type: string - in: path name: uuid description: '' example: d94fca2e-1789-491b-88a9-03b5738dd218 required: true schema: type: string - in: path name: organisation description: 'The organisation UUID.' example: 550e8400-e29b-41d4-a716-446655440000 required: true schema: type: string - in: path name: project description: 'The project UUID.' example: 660e8400-e29b-41d4-a716-446655440001 required: true schema: type: string '/api/organisations/{organisation_uuid}/review-views': get: summary: 'Get All Review Views' operationId: getAllReviewViews description: "List all saved review views for the authenticated user in a specific organisation.\nViews are ordered by sort_order and creation date." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: - uuid: a1b2c3d4-... name: 'Critical Reviews' sort_order: 0 filters: brands: - uuid-1 - uuid-2 platforms: - G2 - Capterra rating_buckets: - 1.0-1.9 - 2.0-2.9 date_range: type: preset value: last_3_months created_at: '2026-01-15T10:30:00.000Z' updated_at: '2026-01-20T14:22:00.000Z' properties: data: type: array example: - uuid: a1b2c3d4-... name: 'Critical Reviews' sort_order: 0 filters: brands: - uuid-1 - uuid-2 platforms: - G2 - Capterra rating_buckets: - 1.0-1.9 - 2.0-2.9 date_range: type: preset value: last_3_months created_at: '2026-01-15T10:30:00.000Z' updated_at: '2026-01-20T14:22:00.000Z' items: type: object properties: uuid: type: string example: a1b2c3d4-... name: type: string example: 'Critical Reviews' sort_order: type: integer example: 0 filters: type: object properties: brands: type: array example: - uuid-1 - uuid-2 items: type: string platforms: type: array example: - G2 - Capterra items: type: string rating_buckets: type: array example: - 1.0-1.9 - 2.0-2.9 items: type: string date_range: type: object properties: type: type: string example: preset value: type: string example: last_3_months created_at: type: string example: '2026-01-15T10:30:00.000Z' updated_at: type: string example: '2026-01-20T14:22:00.000Z' tags: - 'Review Views' post: summary: 'Create Review View' operationId: createReviewView description: 'Create a new saved review view for the authenticated user.' parameters: [] responses: 201: description: '' content: text/plain: schema: type: string example: "{\n \"data\": {\n \"uuid\": \"e5f6g7h8-...\",\n \"name\": \"High Priority Reviews\",\n \"sort_order\": 1,\n \"filters\": {...},\n \"created_at\": \"2026-01-24T10:00:00.000Z\",\n \"updated_at\": \"2026-01-24T10:00:00.000Z\"\n }\n}" tags: - 'Review Views' requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'The name of the view. Must be unique per user/organisation.' example: 'High Priority Reviews' filters: type: object description: 'Filter configuration.' example: platforms: - G2 rating_buckets: - 1.0-1.9 properties: brands: type: array description: 'Filter by brands (project UUIDs).' example: - uuid-1 - uuid-2 items: type: string platforms: type: array description: 'Platforms to filter by.' example: - G2 - Capterra items: type: string date_range: type: object description: 'Date range preset.' example: type: preset value: last_3_months properties: type: type: string description: 'This field is required whenfilters.date_range is present.'
example: preset
enum:
- preset
- custom
- all_time
value:
type: string
description: 'This field is required when filters.date_range.type is preset.'
example: last_3_months
enum:
- last_14_days
- last_3_months
- last_12_months
- last_3_years
- all_time
nullable: true
date_range_custom:
type: object
description: 'Custom date range.'
example:
start: '2026-01-01'
end: '2026-01-24'
properties:
start:
type: string
description: 'This field is required when filters.date_range_custom is present. Must be a valid date.'
example: '2026-02-03T13:58:49'
end:
type: string
description: 'This field is required when filters.date_range_custom is present. Must be a valid date. Must be a date after or equal to filters.date_range_custom.start.'
example: '2052-02-27'
nullable: true
rating_buckets:
type: array
description: 'Rating buckets.'
example:
- '5.0'
- 4.0-4.9
items:
type: string
languages:
type: array
description: 'Language filters (ISO 639-1 codes).'
example:
- en
- de
- es
items:
type: string
read_status:
type: string
description: 'Read status filter.'
example: unread
nullable: true
search:
type: string
description: 'Keyword search.'
example: 'customer support'
nullable: true
sort_by:
type: string
description: ''
example: platform
enum:
- review_date
- rating
- platform
- created_at
nullable: true
sort_direction:
type: string
description: ''
example: asc
enum:
- asc
- desc
nullable: true
required:
- name
- filters
parameters:
-
in: path
name: organisation_uuid
description: 'The organisation UUID.'
example: 660e8400-e29b-41d4-a716-446655440001
required: true
schema:
type: string
'/api/organisations/{organisation_uuid}/review-views/{view_uuid}':
get:
summary: 'Get Review View'
operationId: getReviewView
description: 'Get a specific saved review view.'
parameters: []
responses:
200:
description: ''
content:
text/plain:
schema:
type: string
example: "{\n \"data\": {\n \"uuid\": \"a1b2c3d4-...\",\n \"name\": \"Critical Reviews\",\n \"sort_order\": 0,\n \"filters\": {...},\n \"created_at\": \"2026-01-15T10:30:00.000Z\",\n \"updated_at\": \"2026-01-20T14:22:00.000Z\"\n }\n}"
tags:
- 'Review Views'
put:
summary: 'Update Review View'
operationId: updateReviewView
description: 'Update an existing saved review view. Can update name and/or filters.'
parameters: []
responses:
200:
description: ''
content:
text/plain:
schema:
type: string
example: "{\n \"data\": {\n \"uuid\": \"a1b2c3d4-...\",\n \"name\": \"Critical Reviews - Updated\",\n \"sort_order\": 0,\n \"filters\": {...},\n \"created_at\": \"2026-01-15T10:30:00.000Z\",\n \"updated_at\": \"2026-01-24T11:15:00.000Z\"\n }\n}"
tags:
- 'Review Views'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: 'The name of the view.'
example: 'Critical Reviews - Updated'
filters:
type: object
description: 'Filter configuration.'
example:
platforms:
- G2
properties:
brands:
type: array
description: 'Must be a valid UUID. The uuid of an existing record in the projects table. The uuid of an existing record in the projects table.'
example:
- a4855dc5-0acb-33c3-b921-f4291f719ca0
items:
type: string
platforms:
type: array
description: ''
example:
- 'Chrome Web Store'
items:
type: string
enum:
- g2
- capterra
- software_advice
- trustpilot
- omr_reviews
- clutch
- sourceforge
- product_hunt
- hubspot_directory
- goodfirms
- chrome_web_store
- google_workspace_marketplace
- play_store
- app_store
- subscribed_fyi
- google_reviews
- G2
- Capterra
- 'Software Advice'
- Trustpilot
- 'OMR Reviews'
- Clutch
- SourceForge
- 'Product Hunt'
- 'HubSpot Directory'
- GoodFirms
- 'Chrome Web Store'
- 'Google Workspace Marketplace'
- 'Play Store'
- 'App Store'
- Subscribed.FYI
- 'Google Reviews'
date_range:
type: object
description: ''
example: null
properties:
type:
type: string
description: 'This field is required when filters.date_range is present.'
example: all_time
enum:
- preset
- custom
- all_time
value:
type: string
description: 'This field is required when filters.date_range.type is preset.'
example: last_3_months
enum:
- last_14_days
- last_3_months
- last_12_months
- last_3_years
- all_time
nullable: true
date_range_custom:
type: object
description: ''
example: null
properties:
start:
type: string
description: 'This field is required when filters.date_range_custom is present. Must be a valid date.'
example: '2026-02-03T13:58:49'
end:
type: string
description: 'This field is required when filters.date_range_custom is present. Must be a valid date. Must be a date after or equal to filters.date_range_custom.start.'
example: '2052-02-27'
nullable: true
rating_buckets:
type: array
description: ''
example:
- '5.0'
items:
type: string
enum:
- '5.0'
- 4.0-4.9
- 3.0-3.9
- 2.0-2.9
- 1.0-1.9
languages:
type: array
description: ''
example:
- architecto
items:
type: string
read_status:
type: string
description: ''
example: all
enum:
- all
- read
- unread
nullable: true
search:
type: string
description: 'Must not be greater than 500 characters.'
example: 'n'
nullable: true
sort_by:
type: string
description: ''
example: created_at
enum:
- review_date
- rating
- platform
- created_at
nullable: true
sort_direction:
type: string
description: ''
example: asc
enum:
- asc
- desc
nullable: true
delete:
summary: 'Delete Review View'
operationId: deleteReviewView
description: 'Delete a saved review view.'
parameters: []
responses:
204:
description: ''
content:
application/json:
schema:
type: object
example: { }
properties: { }
tags:
- 'Review Views'
parameters:
-
in: path
name: organisation_uuid
description: 'The organisation UUID.'
example: 660e8400-e29b-41d4-a716-446655440001
required: true
schema:
type: string
-
in: path
name: view_uuid
description: 'The view UUID.'
example: a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6
required: true
schema:
type: string
/api/integrations/slack/callback:
get:
summary: 'Slack OAuth Callback'
operationId: slackOAuthCallback
description: "Handles the OAuth callback from Slack after user authorization.\nExchanges the authorization code for an access token.\nThis endpoint is unauthenticated - uses cached state for auth context."
parameters:
-
in: query
name: code
description: 'The authorization code from Slack.'
example: 123456789.abcdef
required: false
schema:
type: string
description: 'The authorization code from Slack.'
example: 123456789.abcdef
-
in: query
name: state
description: 'The state parameter for CSRF protection.'
example: abc123...
required: false
schema:
type: string
description: 'The state parameter for CSRF protection.'
example: abc123...
-
in: query
name: error
description: 'OAuth error if user denied access.'
example: access_denied
required: false
schema:
type: string
description: 'OAuth error if user denied access.'
example: access_denied
responses:
302:
description: ''
content:
text/plain:
schema:
oneOf:
-
description: Success
type: string
example: 'Redirects to /dashboard/notification/overview?slack_connected=true&workspace={name}'
-
description: Error
type: string
example: 'Redirects to /dashboard/notification/overview?slack_error={error_code}'
-
description: ''
type: string
example: "\n\n \n \n \n\n error is not present.'
example: architecto
state:
type: string
description: 'Must be 64 characters.'
example: ngzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewt
error:
type: string
description: ''
example: architecto
required:
- state
security: []
/api/integrations/slack/connect:
get:
summary: 'Initiate Slack OAuth'
operationId: initiateSlackOAuth
description: "Starts the OAuth flow to connect a Slack workspace.\nRedirects to Slack's authorization page."
parameters: []
responses:
302:
description: 'Redirect to Slack'
content:
text/plain:
schema:
type: string
example: 'Redirects to Slack OAuth page'
400:
description: 'Not configured'
content:
application/json:
schema:
type: object
example:
error: configuration
message: 'Slack integration is not configured.'
properties:
error:
type: string
example: configuration
message:
type: string
example: 'Slack integration is not configured.'
401:
description: ''
content:
application/json:
schema:
type: object
example:
message: Unauthenticated.
properties:
message:
type: string
example: Unauthenticated.
403:
description: 'No organisation'
content:
application/json:
schema:
type: object
example:
message: 'Organisation context required.'
properties:
message:
type: string
example: 'Organisation context required.'
tags:
- 'Slack Integration'
/api/integrations/slack/status:
get:
summary: 'Get Slack Connection Status'
operationId: getSlackConnectionStatus
description: 'Returns the current Slack connection status for the organisation.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: Connected
type: object
example:
connected: true
team_id: T123456789
team_name: 'My Workspace'
scopes: 'chat:write,channels:read'
connected_at: '2025-01-01T12:00:00Z'
properties:
connected:
type: boolean
example: true
team_id:
type: string
example: T123456789
team_name:
type: string
example: 'My Workspace'
scopes:
type: string
example: 'chat:write,channels:read'
connected_at:
type: string
example: '2025-01-01T12:00:00Z'
-
description: 'Not connected'
type: object
example:
connected: false
properties:
connected:
type: boolean
example: false
-
description: 'Invalid token'
type: object
example:
connected: false
error: token_invalid
message: 'Slack connection needs to be re-authorized.'
properties:
connected:
type: boolean
example: false
error:
type: string
example: token_invalid
message:
type: string
example: 'Slack connection needs to be re-authorized.'
403:
description: 'No organisation'
content:
application/json:
schema:
type: object
example:
message: 'Organisation context required.'
properties:
message:
type: string
example: 'Organisation context required.'
tags:
- 'Slack Integration'
/api/integrations/slack/disconnect:
post:
summary: 'Disconnect Slack'
operationId: disconnectSlack
description: 'Removes the Slack workspace connection for the organisation.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
oneOf:
-
description: Success
type: object
example:
success: true
message: 'Slack connection removed.'
properties:
success:
type: boolean
example: true
message:
type: string
example: 'Slack connection removed.'
-
description: 'Not connected'
type: object
example:
success: true
message: 'No Slack connection found.'
properties:
success:
type: boolean
example: true
message:
type: string
example: 'No Slack connection found.'
403:
description: 'No organisation'
content:
application/json:
schema:
type: object
example:
message: 'Organisation context required.'
properties:
message:
type: string
example: 'Organisation context required.'
tags:
- 'Slack Integration'
/api/integrations/slack/channels:
get:
summary: 'List Slack Channels'
operationId: listSlackChannels
description: 'Fetches the list of channels from the connected Slack workspace.'
parameters: []
responses:
200:
description: Success
content:
application/json:
schema:
type: object
example:
ok: true
channels:
-
id: C123456789
name: general
is_member: true
-
id: C987654321
name: random
is_member: false
properties:
ok:
type: boolean
example: true
channels:
type: array
example:
-
id: C123456789
name: general
is_member: true
-
id: C987654321
name: random
is_member: false
items:
type: object
properties:
id:
type: string
example: C123456789
name:
type: string
example: general
is_member:
type: boolean
example: true
401:
description: ''
content:
application/json:
schema:
oneOf:
-
description: 'Not connected'
type: object
example:
ok: false
error: not_connected
message: 'Not connected to Slack.'
properties:
ok:
type: boolean
example: false
error:
type: string
example: not_connected
message:
type: string
example: 'Not connected to Slack.'
-
description: 'Invalid token'
type: object
example:
ok: false
error: token_invalid
message: 'Slack connection needs to be re-authorized.'
properties:
ok:
type: boolean
example: false
error:
type: string
example: token_invalid
message:
type: string
example: 'Slack connection needs to be re-authorized.'
403:
description: 'No organisation'
content:
application/json:
schema:
type: object
example:
message: 'Organisation context required.'
properties:
message:
type: string
example: 'Organisation context required.'
tags:
- 'Slack Integration'
/api/subscription-plans:
get:
summary: 'List Subscription Plans'
operationId: listSubscriptionPlans
description: 'Get all available subscription plans.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
-
id: 1
stripe_price_id: price_1234567890
name: 'Pro Plan'
description: 'Professional features'
amount: 2999
currency: usd
interval: month
properties:
data:
type: array
example:
-
id: 1
stripe_price_id: price_1234567890
name: 'Pro Plan'
description: 'Professional features'
amount: 2999
currency: usd
interval: month
items:
type: object
properties:
id:
type: integer
example: 1
stripe_price_id:
type: string
example: price_1234567890
name:
type: string
example: 'Pro Plan'
description:
type: string
example: 'Professional features'
amount:
type: integer
example: 2999
currency:
type: string
example: usd
interval:
type: string
example: month
tags:
- Subscriptions
/api/subscriptions/checkout:
post:
summary: 'Create Checkout Session'
operationId: createCheckoutSession
description: 'Create a Stripe Checkout session for subscribing to a plan.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
checkout_url: 'https://checkout.stripe.com/pay/cs_test_...'
properties:
checkout_url:
type: string
example: 'https://checkout.stripe.com/pay/cs_test_...'
403:
description: 'No organisation access'
content:
application/json:
schema:
type: object
example:
message: 'You do not have access to this organisation.'
properties:
message:
type: string
example: 'You do not have access to this organisation.'
404:
description: 'Plan not found'
content:
application/json:
schema:
type: object
example:
message: 'Subscription plan not found.'
properties:
message:
type: string
example: 'Subscription plan not found.'
tags:
- Subscriptions
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
plan_id:
type: integer
description: 'The subscription plan ID.'
example: 1
success_url:
type: string
description: 'The URL to redirect to after successful payment.'
example: 'https://app.example.com/subscription/success'
cancel_url:
type: string
description: 'The URL to redirect to if payment is canceled.'
example: 'https://app.example.com/subscription/cancel'
required:
- plan_id
- success_url
- cancel_url
/api/subscriptions/current:
get:
summary: 'Get Current Subscription'
operationId: getCurrentSubscription
description: 'Get the current subscription for the organisation.'
parameters: []
responses:
200:
description: ''
content:
application/json:
schema:
type: object
example:
data:
id: 1
stripe_subscription_id: sub_1234567890
status: active
current_period_start: '2025-12-01T00:00:00.000000Z'
current_period_end: '2026-01-01T00:00:00.000000Z'
plan:
id: 1
stripe_price_id: price_1234567890
description: 'Professional features'
name: 'Pro Plan'
amount: 2999
currency: usd
interval: month
features:
- 'Feature 1'
- 'Feature 2'
properties:
data:
type: object
properties:
id:
type: integer
example: 1
stripe_subscription_id:
type: string
example: sub_1234567890
status:
type: string
example: active
current_period_start:
type: string
example: '2025-12-01T00:00:00.000000Z'
current_period_end:
type: string
example: '2026-01-01T00:00:00.000000Z'
plan:
type: object
properties:
id:
type: integer
example: 1
stripe_price_id:
type: string
example: price_1234567890
description:
type: string
example: 'Professional features'
name:
type: string
example: 'Pro Plan'
amount:
type: integer
example: 2999
currency:
type: string
example: usd
interval:
type: string
example: month
features:
type: array
example:
- 'Feature 1'
- 'Feature 2'
items:
type: string
403:
description: 'No organisation access'
content:
application/json:
schema:
type: object
example:
message: 'You do not have access to this organisation.'
properties:
message:
type: string
example: 'You do not have access to this organisation.'
404:
description: 'No subscription'
content:
application/json:
schema:
type: object
example:
message: 'No subscription found for this organisation.'
properties:
message:
type: string
example: 'No subscription found for this organisation.'
tags:
- Subscriptions