MENU navbar-image

Introduction

This documentation aims to provide all the information you need to work with our API.

Mail test server: https://backend-staging.subsig.com:8025 (HTTP only)

Postman collection: https://backend-staging.subsig.com/docs.postman

OpenAPI spec: https://backend-staging.subsig.com/docs.openapi

Test Users (Development Only)

The following test accounts are available for testing purposes:

Email Password Organisation Role
admin@acme.com password Acme Corporation admin
admin@acme.com password Acme Corporation organisation_owner
member@acme.com password Acme Corporation organisation_member
project@acme.com password Acme Corporation project_member (CRM & Analytics only)
alice@techstart.com password TechStart Inc organisation_owner

Note: admin@acme.com is also a member of TechStart Inc for testing multi-organisation switching.

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer 1|abc123...".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

You can retrieve your token by visiting your dashboard and clicking Generate API token.

Registration

Create a new user account to access the application.

Create Account

Register a new user account. After successful registration, the user will be automatically logged in and redirected to the dashboard.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/register"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "John Doe",
    "email": "john@example.com",
    "password": "SecurePass123!",
    "password_confirmation": "SecurePass123!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, Account created. User logged in and redirected.):



 

Example response (422, Validation error.):


{
    "message": "The email has already been taken.",
    "errors": {
        "email": [
            "The email has already been taken."
        ]
    }
}
 

Request      

POST register

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Full name of the user. Example: John Doe

email   string     

Valid email address. Must be unique. Example: john@example.com

password   string     

Password (min 8 characters, at least one uppercase letter, one lowercase letter, one number and one special character). Example: SecurePass123!

password_confirmation   string     

Must match password exactly. Example: SecurePass123!

Authentication

APIs for user authentication

Create API Token

Generate an API token for authenticated requests. Requires a verified email address.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/sanctum/token"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "john@example.com",
    "password": "SecurePass123!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "token": "1|abc123..."
}
 

Example response (422, Invalid credentials):


{
    "message": "The provided credentials are incorrect.",
    "errors": {
        "email": [
            "The provided credentials are incorrect."
        ]
    }
}
 

Example response (422, Email not verified):


{
    "message": "Please verify your email address before logging in.",
    "errors": {
        "email": [
            "Please verify your email address before logging in."
        ]
    }
}
 

Request      

POST api/sanctum/token

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

The user's email address. Example: john@example.com

password   string     

The user's password. Example: SecurePass123!

Verify Email

Verify user's email address using the 4-digit code sent via email. Returns an API token on successful verification. Verification link is sent via email. /verify-email?code=1234&email=john@example.com

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/email/verify"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "john@example.com",
    "code": "1234"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Email verified successfully.",
    "token": "1|abc123..."
}
 

Example response (200, Already verified):


{
    "message": "Email already verified.",
    "token": "1|abc123..."
}
 

Example response (422, Invalid code):


{
    "message": "Invalid verification code.",
    "errors": {
        "code": [
            "Invalid verification code."
        ]
    }
}
 

Example response (422, Expired code):


{
    "message": "Verification code has expired. Please request a new one.",
    "errors": {
        "code": [
            "Verification code has expired. Please request a new one."
        ]
    }
}
 

Request      

POST api/email/verify

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

The user's email address. Example: john@example.com

code   string     

The 4-digit verification code. Example: 1234

Resend Verification Code

Send a new 4-digit verification code to the user's email. Code expires in 60 minutes.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/email/resend"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "john@example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Verification code sent."
}
 

Example response (200, Already verified):


{
    "message": "Email already verified."
}
 

Example response (422, User not found):


{
    "message": "No account found with this email.",
    "errors": {
        "email": [
            "No account found with this email."
        ]
    }
}
 

Request      

POST api/email/resend

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

The user's email address. Example: john@example.com

Register with Invite

Accept an invitation and create a new user account. The email address must match the email address on the invite. After successful registration, the user will be added to the organisation or project and will receive an email verification code.

Note: This endpoint bypasses the business email requirement since the invitation itself validates the user's legitimacy.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/invites/accept"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "token": "eEtgjrcdtubjCu4817MfGiimvC2DQLBgaI7LpY1g5kdDMK5wJlQank7ZJ6PWurmb",
    "name": "John Doe",
    "email": "user@example.com",
    "password": "SecurePass123!",
    "password_confirmation": "SecurePass123!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "message": "Registration successful. Please check your email to verify your account."
}
 

Example response (404, Token not found):


{
    "message": "Invite not found.",
    "errors": {
        "token": [
            "The invite token is invalid or does not exist."
        ]
    }
}
 

Example response (410, Invite expired):


{
    "message": "Invite has expired.",
    "errors": {
        "token": [
            "This invite has expired. Please request a new invitation."
        ]
    }
}
 

Example response (410, Invite already accepted):


{
    "message": "Invite has already been accepted.",
    "errors": {
        "token": [
            "This invite has already been accepted."
        ]
    }
}
 

Example response (422, Email mismatch):


{
    "message": "The email address does not match the invitation.",
    "errors": {
        "email": [
            "The email address must match the email on the invitation."
        ]
    }
}
 

Example response (422, Validation error):


{
    "message": "The name field is required.",
    "errors": {
        "name": [
            "The name field is required."
        ]
    }
}
 

Example response (422, Email already registered):


{
    "message": "The email has already been taken.",
    "errors": {
        "email": [
            "The email has already been taken."
        ]
    }
}
 

Request      

POST api/invites/accept

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

token   string     

The invite token from the invitation link. Example: eEtgjrcdtubjCu4817MfGiimvC2DQLBgaI7LpY1g5kdDMK5wJlQank7ZJ6PWurmb

name   string     

The user's full name. Example: John Doe

email   string     

The user's email address (must match the invite email). Example: user@example.com

password   string     

The user's password (min 8 characters). Example: SecurePass123!

password_confirmation   string     

Password confirmation. Example: SecurePass123!

Get Current User

requires authentication

Get the authenticated user's details including organisation and subscription information.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/user"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
    }
}
 

Example response (401, Unauthenticated):


{
    "message": "Unauthenticated."
}
 

Request      

GET api/user

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Log In

Authenticate with your email and password to start a session. On success, you receive a token for subsequent requests.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "john@example.com",
    "password": "SecurePass123!",
    "remember": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, Login successful. Session started.):



 

Example response (422, Invalid credentials.):


{
    "message": "These credentials do not match our records.",
    "errors": {
        "email": [
            "These credentials do not match our records."
        ]
    }
}
 

Request      

POST login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Your registered email address. Example: john@example.com

password   string     

Your account password. Example: SecurePass123!

remember   boolean  optional    

Stay logged in for extended period. Example: true

Log Out

requires authentication

End your current session. You will need to log in again to access protected resources.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/logout"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200, Logged out successfully.):



 

Example response (401, Not logged in.):


{
    "message": "Unauthenticated."
}
 

Request      

POST logout

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Password Reset

Recover access to your account if you forgot your password.

Request Password Reset

Send a password reset link to your email. The link expires after 60 minutes. Same response for security even if email not found.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/forgot-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "john@example.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, Reset link sent.):


{
    "status": "We have emailed your password reset link."
}
 

Request      

POST forgot-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Email address associated with your account. Example: john@example.com

Reset Password

Set a new password using the token from your email. Token is valid for 60 minutes.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/reset-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "token": "a1b2c3d4e5f6g7h8i9j0",
    "email": "john@example.com",
    "password": "NewSecurePass123!",
    "password_confirmation": "NewSecurePass123!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, Password reset successful.):


{
    "status": "Your password has been reset."
}
 

Example response (422, Invalid or expired token.):


{
    "message": "This password reset token is invalid.",
    "errors": {
        "email": [
            "This password reset token is invalid."
        ]
    }
}
 

Request      

POST reset-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

token   string     

Reset token from the email link. Example: a1b2c3d4e5f6g7h8i9j0

email   string     

Your account email address. Example: john@example.com

password   string     

Password (min 8 characters, at least one letter and one number). Example: NewSecurePass123!

password_confirmation   string     

Must match new password exactly. Example: NewSecurePass123!

Reviews

APIs for fetching aggregated reviews from multiple platforms, scoped to organizations.

Get Organization Reviews

requires authentication

Fetch and aggregate reviews from multiple projects/brands within an organization. Reviews are fetched from external API, persisted to database, and returned with filters applied. Supports filtering by multiple brands (projects), platforms, ratings, languages, and more.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/reviews"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "brands": [
        "uuid-1",
        "uuid-2"
    ],
    "view_uuid": "a1b2c3d4-e5f6-g7h8-i9j0",
    "platforms": [
        "G2",
        "Capterra"
    ],
    "date_range": {
        "type": "all_time",
        "value": "all_time"
    },
    "date_range_custom": {
        "start": "2026-02-03T13:58:49",
        "end": "2052-02-27"
    },
    "date_from": "2026-01-01",
    "date_to": "2026-01-24",
    "rating_buckets": [
        "5.0",
        "4.0-4.9"
    ],
    "languages": [
        "en",
        "de"
    ],
    "read_status": "unread",
    "search": "customer support",
    "sort_by": "review_date",
    "sort_direction": "desc",
    "period_in_days": 30,
    "page": 1
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "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,
                    "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
        }
    }
}
 

Example response (403):


{
    "message": "You do not have access to this organisation."
}
 

Example response (404):


{
    "message": "Organisation not found."
}
 

Request      

POST api/organisations/{organisation_uuid}/reviews

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organization UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Body Parameters

brands   string[]  optional    

Optional - Filter by brand/project UUIDs. Empty/omitted = all brands.

view_uuid   string  optional    

Optional - Apply filters from a saved view. When provided, all other filters (except search and page) are ignored. Example: a1b2c3d4-e5f6-g7h8-i9j0

platforms   string[]  optional    

Optional - Filter by platforms. Ignored if view_uuid is provided.

date_range   object  optional    
type   string  optional    

This field is required when date_range is present. Example: all_time

Must be one of:
  • preset
  • custom
  • all_time
value   string  optional    

This field is required when date_range.type is preset. Example: all_time

Must be one of:
  • last_14_days
  • last_3_months
  • last_12_months
  • last_3_years
  • all_time
date_range_custom   object  optional    
start   string  optional    

This field is required when date_range_custom is present. Must be a valid date. Example: 2026-02-03T13:58:49

end   string  optional    

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

date_from   string  optional    

Optional - Start date. Ignored if view_uuid is provided. Example: 2026-01-01

date_to   string  optional    

Optional - End date. Ignored if view_uuid is provided. Example: 2026-01-24

rating_buckets   string[]  optional    

Optional - Filter by rating buckets. Ignored if view_uuid is provided.

languages   string[]  optional    

Optional - Filter by languages (ISO 639-1 codes). Ignored if view_uuid is provided.

read_status   string  optional    

Optional - Filter by read status. Ignored if view_uuid is provided. Example: unread

search   string  optional    

Optional - Keyword search. Can be used with view_uuid to search within saved view filters. Example: customer support

sort_by   string  optional    

Example: review_date

Must be one of:
  • review_date
  • rating
  • platform
  • created_at
sort_direction   string  optional    

Example: desc

Must be one of:
  • asc
  • desc
period_in_days   integer  optional    

Optional - Number of days to fetch reviews for. Default: 30. Ignored if view_uuid is provided. Example: 30

page   integer  optional    

Optional - Page number for pagination. Default: 1. Example: 1

Get Review Analytics

requires authentication

Calculate analytics metrics for reviews including new reviews count, average rating, reviews per month, with comparison to previous period. Also includes rating distribution and platform breakdown.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/reviews/analytics"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "brands": [
        "uuid-1",
        "uuid-2"
    ],
    "compare_products": [
        "uuid-1",
        "uuid-2"
    ],
    "platforms": [
        "G2",
        "Capterra"
    ],
    "date_range": {
        "type": "preset",
        "value": "last_3_months"
    },
    "date_range_custom": {
        "start": "2025-10-01",
        "end": "2026-01-27"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "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,
                    "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,
                "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"
}
 

Example response (200, Multi-product comparison):


{
  "data": [
    {
      "product_uuid": "660e8400-e29b-41d4-a716-446655440001",
      "product_name": "Product 1",
      "product_logo": "https://...",
      "period": {
        "start": "2025-10-28",
        "end": "2026-01-27",
        "duration": 3
      },
      "metrics": {
        "new_reviews": {...},
        "average_rating": {...},
        "reviews_per_month": {...},
        "review_velocity": {"interval": "week", "data": [{"group": "...", "count": 0, "cumulative_before": 0, "rating": 4.1}]}
      },
      "breakdown": {
        "ratings": {"5.0": 0, "4.0": 2, "3.0": 0, "2.0": 0, "1.0": 0},
        "platforms": {"G2": 1, "Capterra": 1}
      }
    },
    {
      "product_uuid": "770e8400-e29b-41d4-a716-446655440002",
      "product_name": "Product 2",
      "product_logo": "https://...",
      "period": {...},
      "metrics": {...},
      "breakdown": {...}
    }
  ],
  "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": ["660e8400-e29b-41d4-a716-446655440001", "770e8400-e29b-41d4-a716-446655440002"],
    "platforms": [],
    "date_range": {"type": "preset", "value": "last_3_months"}
  },
  "organisation_id": "660e8400-e29b-41d4-a716-446655440000",
  "organisation_name": "Acme Corp"
}
 

Example response (403):


{
    "message": "You do not have access to this organisation."
}
 

Example response (404):


{
    "message": "Organisation not found."
}
 

Request      

POST api/organisations/{organisation_uuid}/reviews/analytics

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organization UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Body Parameters

brands   string[]  optional    

Optional - Filter by brand/project UUIDs. Empty/omitted = all brands. Ignored if compare_products is provided.

compare_products   string[]  optional    

Optional - Compare up to 2 products. When provided, returns array of analytics per product. Takes precedence over brands.

platforms   string[]  optional    

Optional - Filter by platforms.

date_range   object  optional    

Optional - Preset date range.

type   string  optional    

This field is required when date_range is present. Example: preset

Must be one of:
  • preset
  • all_time
value   string  optional    

This field is required when date_range.type is preset. Example: last_12_months

Must be one of:
  • last_14_days
  • last_3_months
  • last_12_months
  • last_3_years
  • all_time
date_range_custom   object  optional    

Optional - Custom date range.

start   string  optional    

This field is required when date_range_custom is present. Must be a valid date. Example: 2026-02-03T13:58:49

end   string  optional    

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

Social Posts

APIs for fetching and filtering social media mentions from multiple platforms, scoped to organizations.

Get Organization Social Posts

requires authentication

Fetch and aggregate social posts from multiple projects/brands within an organization. Posts are fetched from external Scraper API, persisted to database, and returned with filters applied. Supports filtering by brands, platforms, types, sentiments, keywords, and more.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/social"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "brands": [
        "uuid-1",
        "uuid-2"
    ],
    "view_uuid": "a1b2c3d4-e5f6-g7h8-i9j0",
    "platforms": [
        "reddit",
        "github"
    ],
    "types": [
        "post",
        "comment"
    ],
    "sentiments": [
        "positive",
        "negative"
    ],
    "keywords": [
        "Loom",
        "Figma"
    ],
    "date_range": {
        "type": "preset",
        "value": "last_3_months"
    },
    "date_range_custom": {
        "start": "2026-01-01",
        "end": "2026-01-24"
    },
    "read_status": "unread",
    "search": "customer support",
    "sort_by": "post_date",
    "sort_direction": "desc",
    "page": 1,
    "per_page": 10
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "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"
            ]
        }
    }
}
 

Example response (403):


{
    "message": "You do not have access to this organisation."
}
 

Example response (404):


{
    "message": "Organisation not found."
}
 

Request      

POST api/organisations/{organisation_uuid}/social

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organization UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Body Parameters

brands   string[]  optional    

Optional - Filter by brand/project UUIDs. Empty/omitted = all brands.

view_uuid   string  optional    

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

platforms   string[]  optional    

Optional - Filter by platforms. Ignored if view_uuid is provided.

types   string[]  optional    

Optional - Filter by content type. Ignored if view_uuid is provided.

sentiments   string[]  optional    

Optional - Filter by sentiment. Ignored if view_uuid is provided.

keywords   string[]  optional    

Optional - Filter by keywords. Ignored if view_uuid is provided.

date_range   object  optional    

Optional - Preset date range. Ignored if view_uuid is provided.

type   string  optional    

This field is required when date_range is present. Example: custom

Must be one of:
  • preset
  • custom
  • all_time
value   string  optional    

This field is required when date_range.type is preset. Example: last_3_years

Must be one of:
  • last_14_days
  • last_3_months
  • last_12_months
  • last_3_years
  • all_time
date_range_custom   object  optional    

Optional - Custom date range. Ignored if view_uuid is provided.

start   string  optional    

This field is required when date_range_custom is present. Must be a valid date. Example: 2026-02-03T13:58:49

end   string  optional    

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

read_status   string  optional    

Optional - Filter by read status. Ignored if view_uuid is provided. Example: unread

search   string  optional    

Optional - Keyword search. Can be used with view_uuid to search within saved view filters. Example: customer support

sort_by   string  optional    

Optional - Sort field. Ignored if view_uuid is provided. Example: post_date

sort_direction   string  optional    

Optional - Sort direction. Ignored if view_uuid is provided. Example: desc

page   integer  optional    

Optional - Page number for pagination. Default: 1. Example: 1

per_page   integer  optional    

Optional - Items per page. Default: 10. Maximum: 100. Example: 10

Organisation-level social analytics.

Returns mention count and average sentiment with period-over-period comparison. Filters: brands (social_posts.brand_id), platforms, date_range, date_range_custom (start/end, same as social posts), or custom_date_range (from/to).

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/b47b1b40-d5f7-4e9c-959a-b7affbc9965c/social/analytics"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "brands": [
        16
    ],
    "platforms": [
        "reddit"
    ],
    "date_range": {
        "type": "all_time",
        "value": "last_3_years"
    },
    "custom_date_range": {
        "from": "2026-02-03T13:58:49",
        "to": "2052-02-27"
    },
    "date_range_custom": {
        "start": "2026-02-03T13:58:49",
        "end": "2052-02-27"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Request      

POST api/organisations/{organisation_uuid}/social/analytics

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

Example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c

Body Parameters

brands   integer[]  optional    
platforms   string[]     
Must be one of:
  • reddit
  • github
  • stackoverflow
  • hackernews
date_range   object  optional    
type   string  optional    

This field is required when date_range is present. Example: all_time

Must be one of:
  • preset
  • all_time
value   string  optional    

This field is required when date_range.type is preset. Example: last_3_years

Must be one of:
  • last_14_days
  • last_3_months
  • last_12_months
  • last_3_years
  • all_time
custom_date_range   object  optional    
from   string  optional    

This field is required when custom_date_range is present. Must be a valid date. Example: 2026-02-03T13:58:49

to   string  optional    

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

date_range_custom   object  optional    
start   string  optional    

This field is required when date_range_custom is present. Must be a valid date. Example: 2026-02-03T13:58:49

end   string  optional    

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

List Social Post Views

requires authentication

Get all saved filter views for the current user in this organization.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/social/views"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "uuid": "a1b2c3d4-e5f6-g7h8-i9j0",
            "name": "Critical Alerts",
            "filters": {
                "platforms": [
                    "reddit"
                ],
                "sentiments": [
                    "negative"
                ]
            },
            "created_at": "2026-01-26T10:00:00.000000Z"
        }
    ]
}
 

Request      

GET api/organisations/{organisation_uuid}/social/views

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organization UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Store Social Post View

requires authentication

Create a new saved filter view for social posts.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/social/views"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Critical Alerts",
    "filters": {
        "platforms": [
            "reddit"
        ],
        "sentiments": [
            "negative"
        ],
        "brands": [
            "6ff8f7f6-1eb3-3525-be4a-3932c805afed"
        ],
        "types": [
            "comment"
        ],
        "keywords": [
            "g"
        ],
        "date_range": {
            "type": "custom",
            "value": "all_time"
        },
        "date_range_custom": {
            "start": "2026-02-03T13:58:49",
            "end": "2052-02-27"
        },
        "read_status": "unread",
        "search": "n",
        "sort_by": "comments_count",
        "sort_direction": "asc"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "data": {
        "uuid": "a1b2c3d4-e5f6-g7h8-i9j0",
        "name": "Critical Alerts",
        "filters": {
            "platforms": [
                "reddit"
            ],
            "sentiments": [
                "negative"
            ]
        },
        "created_at": "2026-01-26T10:00:00.000000Z"
    }
}
 

Request      

POST api/organisations/{organisation_uuid}/social/views

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organization UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Body Parameters

name   string     

The name of the view. Example: Critical Alerts

filters   object     

Filter configuration.

brands   string[]  optional    

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.

platforms   string[]  optional    
Must be one of:
  • reddit
  • github
  • stackoverflow
  • hackernews
types   string[]  optional    
Must be one of:
  • post
  • comment
sentiments   string[]  optional    
Must be one of:
  • positive
  • neutral
  • negative
keywords   string[]  optional    

Must not be greater than 255 characters.

date_range   object  optional    
type   string  optional    

This field is required when filters.date_range is present. Example: custom

Must be one of:
  • preset
  • custom
  • all_time
value   string  optional    

This field is required when filters.date_range.type is preset. Example: all_time

Must be one of:
  • last_14_days
  • last_3_months
  • last_12_months
  • last_3_years
  • all_time
date_range_custom   object  optional    
start   string  optional    

This field is required when filters.date_range_custom is present. Must be a valid date. Example: 2026-02-03T13:58:49

end   string  optional    

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

read_status   string  optional    

Example: unread

Must be one of:
  • all
  • read
  • unread
search   string  optional    

Must not be greater than 500 characters. Example: n

sort_by   string  optional    

Example: comments_count

Must be one of:
  • post_date
  • sentiment
  • platform
  • upvotes_count
  • comments_count
  • created_at
sort_direction   string  optional    

Example: asc

Must be one of:
  • asc
  • desc

Show Social Post View

requires authentication

Get a specific saved filter view.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/social/views/6ff8f7f6-1eb3-3525-be4a-3932c805afed"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": {
        "uuid": "a1b2c3d4-e5f6-g7h8-i9j0",
        "name": "Critical Alerts",
        "filters": {
            "platforms": [
                "reddit"
            ],
            "sentiments": [
                "negative"
            ]
        },
        "created_at": "2026-01-26T10:00:00.000000Z"
    }
}
 

Request      

GET api/organisations/{organisation_uuid}/social/views/{view_uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organization UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

view_uuid   string     

The view UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Update Social Post View

requires authentication

Update a saved filter view.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/social/views/6ff8f7f6-1eb3-3525-be4a-3932c805afed"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "sort_order": 27,
    "name": "architecto",
    "filters": {
        "brands": [
            "6b72fe4a-5b40-307c-bc24-f79acf9a1bb9"
        ],
        "platforms": [
            "github"
        ],
        "types": [
            "comment"
        ],
        "sentiments": [
            "neutral"
        ],
        "keywords": [
            "m"
        ],
        "date_range": {
            "type": "all_time",
            "value": "last_3_months"
        },
        "date_range_custom": {
            "start": "2026-02-03T13:58:49",
            "end": "2052-02-27"
        },
        "read_status": "all",
        "search": "n",
        "sort_by": "platform",
        "sort_direction": "asc"
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "data": {
        "uuid": "a1b2c3d4-e5f6-g7h8-i9j0",
        "name": "Critical Alerts Updated",
        "filters": {
            "platforms": [
                "reddit"
            ],
            "sentiments": [
                "negative"
            ]
        },
        "updated_at": "2026-01-26T11:00:00.000000Z"
    }
}
 

Request      

PUT api/organisations/{organisation_uuid}/social/views/{view_uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organization UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

view_uuid   string     

The view UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Body Parameters

sort_order   integer  optional    

Must be at least 0. Example: 27

name   string  optional    

optional The name of the view. Example: architecto

filters   object  optional    

optional Filter configuration.

brands   string[]  optional    

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.

platforms   string[]  optional    
Must be one of:
  • reddit
  • github
  • stackoverflow
  • hackernews
types   string[]  optional    
Must be one of:
  • post
  • comment
sentiments   string[]  optional    
Must be one of:
  • positive
  • neutral
  • negative
keywords   string[]  optional    

Must not be greater than 255 characters.

date_range   object  optional    
type   string  optional    

This field is required when filters.date_range is present. Example: all_time

Must be one of:
  • preset
  • custom
  • all_time
value   string  optional    

This field is required when filters.date_range.type is preset. Example: last_3_months

Must be one of:
  • last_14_days
  • last_3_months
  • last_12_months
  • last_3_years
  • all_time
date_range_custom   object  optional    
start   string  optional    

This field is required when filters.date_range_custom is present. Must be a valid date. Example: 2026-02-03T13:58:49

end   string  optional    

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

read_status   string  optional    

Example: all

Must be one of:
  • all
  • read
  • unread
search   string  optional    

Must not be greater than 500 characters. Example: n

sort_by   string  optional    

Example: platform

Must be one of:
  • post_date
  • sentiment
  • platform
  • upvotes_count
  • comments_count
  • created_at
sort_direction   string  optional    

Example: asc

Must be one of:
  • asc
  • desc

Delete Social Post View

requires authentication

Delete a saved filter view.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/social/views/6ff8f7f6-1eb3-3525-be4a-3932c805afed"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (204):

Empty response
 

Request      

DELETE api/organisations/{organisation_uuid}/social/views/{view_uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organization UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

view_uuid   string     

The view UUID. Example: 6ff8f7f6-1eb3-3525-be4a-3932c805afed

Endpoints

POST api/webhook

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/webhook"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Request      

POST api/webhook

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Invites

APIs for managing invites

Validate Invite

Validate an invite token and return the associated email if the invite is valid. This endpoint is public and does not require authentication.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/invites/validate"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "token": "abc123def456..."
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "email": "user@example.com"
}
 

Example response (404, Invite not found):


{
    "message": "Invite not found."
}
 

Example response (410, Invite expired):


{
    "message": "This invite has expired."
}
 

Example response (410, Invite no longer valid):


{
    "message": "This invite is no longer valid."
}
 

Request      

POST api/invites/validate

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

token   string     

The invite token. Example: abc123def456...

List Invites

requires authentication

Get invites based on context:

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/invites"
);

const params = {
    "project_id": "660e8400-e29b-41d4-a716-446655440001",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "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"
    }
]
 

Example response (403, No access to project):


{
    "message": "You do not have access to this project."
}
 

Example response (404, Project not found):


{
    "message": "Project not found."
}
 

Example response (422, No organisation context):


{
    "message": "No organisation context found.",
    "errors": {
        "organisation": [
            "Please select an organisation or set current organisation."
        ]
    }
}
 

Request      

GET api/invites

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

project_id   string  optional    

Optional. Filter invites by project UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Create Invite

requires authentication

Create a new invite for a user to join an organisation or project. For organisation invites, the organisation is automatically derived from the authenticated user's current organisation context.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/invites"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "user@example.com",
    "type": "organisation",
    "project_id": "660e8400-e29b-41d4-a716-446655440001"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "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"
    }
}
 

Example response (422, No organisation context):


{
    "message": "No organisation context found.",
    "errors": {
        "organisation": [
            "Please select an organisation or set current organisation."
        ]
    }
}
 

Request      

POST api/invites

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

The email address to send the invite to. Example: user@example.com

type   string     

The type of invite (organisation or project). Example: organisation

project_id   string  optional    

The project UUID (required for project invites). Example: 660e8400-e29b-41d4-a716-446655440001

Delete Invite

requires authentication

Delete an invite. Only the inviter or organisation owners can delete invites.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/invites/1"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Invite deleted successfully."
}
 

Example response (403, Not authorized):


{
    "message": "You are not authorized to delete this invite."
}
 

Example response (404, Invite not found):


{
    "message": "Invite not found."
}
 

Example response (422, No organisation context):


{
    "message": "No organisation context found.",
    "errors": {
        "organisation": [
            "Please select an organisation or set current organisation."
        ]
    }
}
 

Request      

DELETE api/invites/{invite_id}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

invite_id   integer     

The invite ID. Example: 1

Links

APIs for managing project links

requires authentication

Get all links associated with a specific project.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/projects/660e8400-e29b-41d4-a716-446655440001/links"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
        }
    ]
}
 

Example response (403, No Access):


{
    "message": "You do not have access to this project."
}
 

Example response (404, Project Not Found):


{
    "message": "Project not found."
}
 

Notifications

APIs for managing notification rules

List Notifications

requires authentication

Get all notification rules for the current organisation.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/notifications"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "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"
    }
]
 

Request      

GET api/notifications

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Notification

requires authentication

Create a new notification rule with channels and project mappings.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/notifications"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Daily Review Alerts",
    "trigger_type": "new_review",
    "is_active": true,
    "languages": [
        "en",
        "es",
        "fr",
        "de"
    ],
    "auto_translate": true,
    "rating_filters": [
        1,
        2,
        3,
        4,
        5
    ],
    "sentiment_filters": [
        "positive",
        "neutral",
        "negative"
    ],
    "keyword": [
        "ai",
        "support"
    ],
    "content_type": "post",
    "notification_frequency": "instant",
    "read_status": "all",
    "condition_filters": {
        "logic": "and",
        "rules": [
            {
                "field": "platforms",
                "operator": "contains",
                "value": "[\"g2\", \"capterra\"]",
                "logic": "or",
                "rules": [
                    []
                ]
            }
        ]
    },
    "channels": [
        {
            "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": [
        {
            "project_uuid": "5f1812b1-15f7-432d-996a-9ab5cfbe01d6",
            "is_active": true,
            "platforms": {
                "review": [
                    "g2",
                    "capterra",
                    "product_hunt",
                    "google_play"
                ],
                "social": [
                    "reddit",
                    "hackernews",
                    "linkedin"
                ]
            },
            "product_logo": "https:\/\/cdn.example.com\/logo.png"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, Success):


{
    "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"
    }
}
 

Example response (422, Validation Error):


{
    "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."
        ]
    }
}
 

Request      

POST api/notifications

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

The notification name. Example: Daily Review Alerts

trigger_type   string  optional    

The trigger type. Defaults to new_review. Example: new_review

is_active   boolean  optional    

Whether the notification is active. Defaults to true. Example: true

languages   string[]  optional    

Language filters (empty = all languages).

auto_translate   boolean  optional    

Auto-translate to English. Defaults to false. Example: true

rating_filters   integer[]  optional    

Rating filters 1-5 (empty = all stars).

sentiment_filters   string[]  optional    

Sentiment filters (empty = all).

keyword   string[]  optional    

Keyword filters (empty or omitted = all).

content_type   string  optional    

Content type: post, comment, all. Defaults to all. Example: post

notification_frequency   string  optional    

Notification delivery frequency: instant, daily. Defaults to instant. Example: instant

read_status   string  optional    

Filter by read status: all, read, unread. Defaults to all. Example: all

condition_filters   object  optional    

Advanced condition filters with AND/OR logic.

logic   string     

Logic operator for combining rules: and, or. Example: and

rules   object[]     

Array of filter rules or nested groups.

field   string  optional    

Filter field: brands, platforms, read_status, keywords, type, sentiment, content, user. Example: platforms

operator   string  optional    

Filter operator: contains, not_contains. Example: contains

value   mixed  optional    

Filter value (string or array depending on field). Example: ["g2", "capterra"]

logic   string  optional    

For nested groups: and, or. Example: or

rules   object[]  optional    

Nested rules array for grouped conditions.

channels   object[]     

Delivery channels configuration. At least one required.

channel_type   string     

Channel type: slack, email, ms_teams, lark, webhook. Example: slack

is_active   boolean  optional    

Whether channel is active. Example: true

config   object  optional    

Channel-specific configuration.

channel_names   string[]  optional    

Slack channel names.

channel_url   string  optional    

Slack channel webhook URL. Example: https://hooks.slack.com/services/xxx

recipients   string[]  optional    

Email recipients (required if email channel is active).

url   string  optional    

Custom webhook URL (required if webhook channel is active). Example: https://your-server.com/webhook/notifications

projects   object[]     

Project/brand mappings. At least one required.

project_uuid   string     

The project UUID. Example: 5f1812b1-15f7-432d-996a-9ab5cfbe01d6

is_active   boolean  optional    

Whether project mapping is active. Example: true

platforms   object  optional    

Platform selections for this project.

review   string[]  optional    

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.

social   string[]  optional    

Social platforms to monitor.

product_logo   string  optional    

Logo URL for the project (optional). Example: https://cdn.example.com/logo.png

Get Notification

requires authentication

Get details of a specific notification.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/notifications/770e8400-e29b-41d4-a716-446655440000"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
}
 

Request      

GET api/notifications/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

The notification UUID. Example: 770e8400-e29b-41d4-a716-446655440000

Update Notification

requires authentication

Update an existing notification rule with channels and project mappings.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/notifications/770e8400-e29b-41d4-a716-446655440000"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Daily Review Alerts",
    "trigger_type": "new_review",
    "is_active": true,
    "languages": [
        "en",
        "es",
        "fr",
        "de"
    ],
    "auto_translate": true,
    "rating_filters": [
        1,
        2,
        3,
        4,
        5
    ],
    "sentiment_filters": [
        "positive",
        "neutral",
        "negative"
    ],
    "keyword": [
        "ai",
        "support"
    ],
    "content_type": "post",
    "notification_frequency": "instant",
    "read_status": "all",
    "condition_filters": {
        "logic": "and",
        "rules": [
            {
                "field": "platforms",
                "operator": "contains",
                "value": "[\"g2\", \"capterra\"]"
            }
        ]
    },
    "channels": [
        {
            "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": [
        {
            "project_uuid": "5f1812b1-15f7-432d-996a-9ab5cfbe01d6",
            "is_active": true,
            "platforms": {
                "review": [
                    "software_advice"
                ],
                "social": [
                    "reddit"
                ]
            },
            "product_logo": "g"
        }
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, Success):


{
    "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"
    }
}
 

Example response (404, Not Found):


{
    "message": "Notification not found."
}
 

Request      

PUT api/notifications/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

The notification UUID. Example: 770e8400-e29b-41d4-a716-446655440000

Body Parameters

name   string     

The notification name. Example: Daily Review Alerts

trigger_type   string  optional    

The trigger type. Example: new_review

is_active   boolean  optional    

Whether the notification is active. Example: true

languages   string[]  optional    

Language filters (empty = all languages).

auto_translate   boolean  optional    

Auto-translate to English. Example: true

rating_filters   integer[]  optional    

Rating filters 1-5.

sentiment_filters   string[]  optional    

Sentiment filters.

keyword   string[]  optional    

Keyword filters (empty or omitted = all).

content_type   string  optional    

Content type: post, comment, all. Defaults to all. Example: post

notification_frequency   string  optional    

Notification delivery frequency: instant, daily. Example: instant

read_status   string  optional    

Filter by read status: all, read, unread. Example: all

condition_filters   object  optional    

Advanced condition filters with AND/OR logic.

logic   string     

Logic operator for combining rules: and, or. Example: and

rules   object[]     

Array of filter rules or nested groups.

field   string  optional    

Filter field: brands, platforms, read_status, keywords, type, sentiment, content, user. Example: platforms

operator   string  optional    

Filter operator: contains, not_contains. Example: contains

value   mixed  optional    

Filter value (string or array depending on field). Example: ["g2", "capterra"]

channels   object[]     

Delivery channels configuration.

channel_type   string     

Channel type: slack, email, ms_teams, lark, webhook. Example: slack

is_active   boolean  optional    

Whether channel is active. Example: true

config   object  optional    

Channel-specific configuration.

channel_names   string[]  optional    

Slack channel names.

channel_url   string  optional    

Slack channel webhook URL. Example: https://hooks.slack.com/services/xxx

recipients   string[]  optional    

Email recipients (required if email channel is active).

url   string  optional    

Custom webhook URL (required if webhook channel is active). Example: https://your-server.com/webhook/notifications

projects   object[]     

Project/brand mappings.

project_uuid   string     

The project UUID. Example: 5f1812b1-15f7-432d-996a-9ab5cfbe01d6

is_active   boolean  optional    

Whether project mapping is active. Example: true

platforms   object  optional    

Platform selections for this project.

review   string[]  optional    
Must be one of:
  • 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   string[]  optional    
Must be one of:
  • reddit
  • github
  • stackoverflow
  • hackernews
  • youtube
  • linkedin
  • x
product_logo   string  optional    

Must not be greater than 2048 characters. Example: g

Delete Notification

requires authentication

Delete a notification and all associated channels and project mappings.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/notifications/770e8400-e29b-41d4-a716-446655440000"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Notification deleted successfully."
}
 

Example response (404):


{
    "message": "Notification not found."
}
 

Request      

DELETE api/notifications/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

The notification UUID. Example: 770e8400-e29b-41d4-a716-446655440000

Onboarding

APIs for user onboarding flow

Complete Onboarding

requires authentication

Create an organisation and project in a single step during onboarding. The authenticated user becomes the organisation owner.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/onboarding"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organisation_name": "Acme Inc",
    "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": {
        "g2": {
            "enabled": true,
            "url": "https:\/\/g2.com\/products\/acme"
        },
        "capterra": {
            "enabled": false,
            "url": null
        }
    },
    "reddit_keywords": [
        "acme",
        "acme crm"
    ],
    "reddit_brand_name": "Acme"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "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
        }
    }
}
 

Example response (422, Validation error):


{
    "message": "The organisation name field is required.",
    "errors": {
        "organisation_name": [
            "The organisation name field is required."
        ]
    }
}
 

Request      

POST api/onboarding

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

organisation_name   string     

The organisation/company name. Example: Acme Inc

product_name   string     

The product name. Example: Acme CRM

product_website   string  optional    

The product website URL. Example: https://acme.com

product_logo   string  optional    

The product logo URL. Example: https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID

review_platforms   object  optional    

Review platform settings with enabled status and URLs.

reddit_keywords   string[]  optional    

Reddit keyword variations to track.

reddit_brand_name   string  optional    

The brand name for Reddit tracking. Example: Acme

Update Onboarding

requires authentication

Update the current organisation and project for the authenticated user.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/onboarding"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "organisation_name": "Acme Inc",
    "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": {
        "g2": {
            "enabled": true,
            "url": "https:\/\/g2.com\/products\/acme"
        },
        "capterra": {
            "enabled": false,
            "url": null
        }
    },
    "reddit_keywords": [
        "acme",
        "acme crm"
    ],
    "reddit_brand_name": "Acme"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "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
        }
    }
}
 

Example response (404, No organisation or project found):


{
    "message": "No organisation or project found for the current user."
}
 

Example response (422, Validation error):


{
    "message": "The organisation name field is required.",
    "errors": {
        "organisation_name": [
            "The organisation name field is required."
        ]
    }
}
 

Request      

PUT api/onboarding

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

organisation_name   string     

The organisation/company name. Example: Acme Inc

product_name   string     

The product name. Example: Acme CRM

product_website   string  optional    

The product website URL. Example: https://acme.com

product_logo   string  optional    

The product logo URL. Example: https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID

review_platforms   object  optional    

Review platform settings with enabled status and URLs.

reddit_keywords   string[]  optional    

Reddit keyword variations to track.

reddit_brand_name   string  optional    

The brand name for Reddit tracking. Example: Acme

Search Platform URLs

requires authentication

Search for a product's listing pages across multiple review platforms using Perplexity AI-powered web search. Each URL is validated and assigned a status: valid, invalid, or needs_review.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/platform-urls"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "product_name": "Slack",
    "product_website": "https:\/\/slack.com",
    "platform": "g2"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200, All platforms):


{
    "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"
        }
    }
}
 

Example response (200, Filtered by platform):


{
    "product_name": "Slack",
    "product_website": "https://slack.com",
    "reviews": {
        "g2": {
            "url": "https://www.g2.com/products/slack/reviews",
            "status": "valid"
        }
    }
}
 

Example response (422, Validation error):


{
    "message": "The product name field is required.",
    "errors": {
        "product_name": [
            "The product name field is required."
        ]
    }
}
 

Request      

POST api/platform-urls

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

product_name   string     

The name of the product to search for. Example: Slack

product_website   string     

The product's website URL. Example: https://slack.com

platform   string  optional    

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

Generate Reddit Keywords

requires authentication

Generate commonly used variations of a brand name for Reddit tracking, including misspellings, abbreviations, and nicknames.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/reddit-keywords"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "brand_name": "Salesforce"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "brand_name": "Salesforce",
    "top_variations": {
        "exact": "Salesforce",
        "misspellings": [
            "Salesfoce",
            "Salseforce",
            "Saleforce"
        ],
        "abbreviations": [
            "SF",
            "SFDC"
        ],
        "nicknames": [
            "The Force",
            "SF CRM",
            "Sales Cloud"
        ]
    }
}
 

Example response (422, Validation error):


{
    "message": "The brand name field is required.",
    "errors": {
        "brand_name": [
            "The brand name field is required."
        ]
    }
}
 

Request      

POST api/reddit-keywords

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

brand_name   string     

The brand name to generate variations for. Example: Salesforce

Organisations

APIs for managing organisations

List Organisations

requires authentication

Get all organisations the authenticated user belongs to.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
        }
    ]
}
 

Request      

GET api/organisations

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Organisation

requires authentication

Create a new organisation. The authenticated user becomes the admin.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Acme Inc",
    "website": "https:\/\/acme.com"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "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"
    }
}
 

Request      

POST api/organisations

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

The organisation name. Example: Acme Inc

website   string  optional    

optional The organisation website URL. Example: https://acme.com

Get Organisation

requires authentication

Get details of a specific organisation.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/b47b1b40-d5f7-4e9c-959a-b7affbc9965c"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
    }
}
 

Example response (403, No access):


{
    "message": "You do not have access to this organisation."
}
 

Request      

GET api/organisations/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

Example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c

organisation   string     

The organisation UUID. Example: 550e8400-e29b-41d4-a716-446655440000

Update Organisation

requires authentication

Update an organisation's details. Requires admin role.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/b47b1b40-d5f7-4e9c-959a-b7affbc9965c"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Acme Corp",
    "website": "https:\/\/acme.com"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "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"
    }
}
 

Example response (403, Not admin):


{
    "message": "You must be an organisation admin to perform this action."
}
 

Request      

PUT api/organisations/{uuid}

PATCH api/organisations/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

Example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c

organisation   string     

The organisation UUID. Example: 550e8400-e29b-41d4-a716-446655440000

Body Parameters

name   string     

The organisation name. Example: Acme Corp

website   string  optional    

optional The organisation website URL. Example: https://acme.com

Switch Current Organisation

requires authentication

Set the user's current organisation for subsequent requests.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/b47b1b40-d5f7-4e9c-959a-b7affbc9965c/switch"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Switched to organisation successfully.",
    "data": {
        "uuid": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Acme Inc"
    }
}
 

Example response (403, No access):


{
    "message": "You do not have access to this organisation."
}
 

Request      

POST api/organisations/{organisation_uuid}/switch

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

Example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c

organisation   string     

The organisation UUID. Example: 550e8400-e29b-41d4-a716-446655440000

Platforms

APIs for managing platforms

List All Platforms

requires authentication

Get all platforms.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/platforms"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "uuid": "550e8400-e29b-41d4-a716-446655440000",
        "name": "G2",
        "created_at": "2026-01-08T10:00:00.000000Z",
        "updated_at": "2026-01-08T10:00:00.000000Z"
    }
]
 

Request      

GET api/platforms

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Platform

requires authentication

Create a new platform.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/platforms"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "G2"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "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"
    }
}
 

Example response (422, Validation Error):


{
    "message": "The name has already been taken.",
    "errors": {
        "name": [
            "The name has already been taken."
        ]
    }
}
 

Request      

POST api/platforms

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

The platform name. Example: G2

Update Platform

requires authentication

Update a platform's details.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/platforms/550e8400-e29b-41d4-a716-446655440000"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Capterra"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "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"
    }
}
 

Example response (404, Not Found):


{
    "message": "Platform not found."
}
 

Example response (422, Validation Error):


{
    "message": "The name has already been taken.",
    "errors": {
        "name": [
            "The name has already been taken."
        ]
    }
}
 

Request      

PATCH api/platforms/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

The platform UUID. Example: 550e8400-e29b-41d4-a716-446655440000

Body Parameters

name   string     

The platform name. Example: Capterra

Delete Platform

requires authentication

Delete a platform.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/platforms/550e8400-e29b-41d4-a716-446655440000"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Platform deleted successfully."
}
 

Example response (404, Not Found):


{
    "message": "Platform not found."
}
 

Request      

DELETE api/platforms/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

uuid   string     

The platform UUID. Example: 550e8400-e29b-41d4-a716-446655440000

Profiles

APIs for managing product claims and product profile data.

The Profiles API group covers two related workflows:

  1. Claim Profiles - Submit and manage ownership claims for products
  2. Product Profiles - Edit and sync detailed product information

Claim Profiles

Claim profiles allow users to claim ownership of products listed in the system.

Once a claim is submitted, it goes through a review process. Approved claims grant access to edit the product's profile data.

Claim Statuses:

Staging Environment:

In staging, use test products from Curiosity (names ending with _test):

Configure via CURIOSITY_TEST_PRODUCT_IDS and CURIOSITY_RESTRICT_TO_TEST_PRODUCTS environment variables.

requires authentication

Search for products in the Curiosity database to claim.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/products/search"
);

const params = {
    "q": "slack",
    "limit": "10",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": {
        "products": [
            {
                "id": 123,
                "name": "Slack",
                "url": "https://slack.com",
                "logo_path": "products/slack-logo.png"
            }
        ]
    }
}
 

List All Claim Profiles

requires authentication

Get all claim profiles across all organisations. Intended for admin panel usage.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/claim-profiles"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


[
    {
        "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"
    }
]
 

Request      

GET api/claim-profiles

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Claim Profile

requires authentication

Create a new claim profile for a project. The organisation is automatically derived from the authenticated user's current organisation context.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/claim-profiles"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "project_id": "660e8400-e29b-41d4-a716-446655440001",
    "scraper_product_id": 123,
    "product_name": "Slack",
    "product_url": "https:\/\/slack.com",
    "business_email": "john@company.com",
    "job_title": "Product Manager",
    "business_phone": "+1234567890"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, Success):


{
    "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"
    }
}
 

Example response (403, No organisation context):


{
    "message": "This action is unauthorized."
}
 

Example response (422, Validation Error):


{
    "message": "The project is required. (and 1 more error)",
    "errors": {
        "project_id": [
            "The project is required."
        ],
        "business_email": [
            "Please use a business email address."
        ]
    }
}
 

Request      

POST api/claim-profiles

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

project_id   string     

The project UUID. Example: 660e8400-e29b-41d4-a716-446655440001

scraper_product_id   integer     

The ID of the product from Curiosity. Example: 123

product_name   string     

The name of the product being claimed. Example: Slack

product_url   string  optional    

The URL of the product. Example: https://slack.com

business_email   string     

The business email address. Example: john@company.com

job_title   string     

The job title. Example: Product Manager

business_phone   string  optional    

The business phone number (optional). Example: +1234567890

Create Claim Profile (External)

Create a new claim profile from an external website. This endpoint does not require user authentication but requires a valid external API key.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/external/claim-profiles"
);

const headers = {
    "Authorization": "required The external API key. Example: Bearer your-secure-external-api-key",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "scraper_product_id": 123,
    "product_name": "Lovable",
    "product_url": "https:\/\/lovable.dev",
    "business_email": "john@company.com",
    "job_title": "Product Manager",
    "business_phone": "+1234567890"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201, Success):


{
    "message": "Claim profile submitted successfully.",
    "data": {
        "uuid": "880e8400-e29b-41d4-a716-446655440000",
        "product_name": "Lovable",
        "business_email": "john@company.com",
        "status": "pending"
    }
}
 

Example response (401, Unauthorized):


{
    "message": "Unauthorized."
}
 

Example response (422, Validation Error):


{
    "message": "The business email is required.",
    "errors": {
        "business_email": [
            "The business email is required."
        ]
    }
}
 

Request      

POST api/external/claim-profiles

Headers

Authorization        

Example: required The external API key. Example: Bearer your-secure-external-api-key

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

scraper_product_id   integer     

The ID of the product from Curiosity. Example: 123

product_name   string     

The name of the product being claimed. Example: Lovable

product_url   string  optional    

The URL of the product. Example: https://lovable.dev

business_email   string     

The business email address. Example: john@company.com

job_title   string  optional    

The job title. Example: Product Manager

business_phone   string  optional    

The business phone number (optional). Example: +1234567890

Product Profiles

Product profiles contain detailed information about claimed products, synced bidirectionally with the Curiosity database.

Product profiles are created automatically when a claim is approved. They include:

Sync Operations:

Staging Environment:

Sync operations are restricted to test products. Configure via:

Get Product Profile

requires authentication

Get the product profile for a specific project. Returns all profile data including content, SEO meta, categories, and sync status.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/projects/660e8400-e29b-41d4-a716-446655440001/profile"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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": "<p>Where work happens</p>",
        "overview": "<p>Slack is a messaging platform...</p>",
        "pricing": "<p>Free, Pro $7.25/user, Business+ $12.50/user</p>",
        "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": "<p>To cancel your subscription...</p>",
        "cancellation_content_summary": "<p>Cancel anytime from settings</p>",
        "book_demo_url": "https://slack.com/demo",
        "pricing_url": "https://slack.com/pricing",
        "pros_cons": "Pros: Easy to use\nCons: Can be expensive",
        "analysis": "<p>Detailed analysis of Slack...</p>",
        "faq": "Q: How much does it cost?\nA: Free tier available",
        "alternatives_text": "<p>Consider Microsoft Teams or Discord...</p>",
        "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"
    }
}
 

Example response (404):


{
    "message": "Project not found."
}
 

Example response (404):


{
    "message": "This project does not have a product profile yet."
}
 

Request      

GET api/projects/{project_uuid}/profile

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

project_uuid   string     

The project UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Update Product Profile

requires authentication

Submit a change request for the product profile. Changes are stored as a pending request for admin review. Once approved, changes will be applied and synced to Curiosity. Fields like subtitle, overview, pricing, analysis, and alternatives_text support HTML/richtext.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/projects/660e8400-e29b-41d4-a716-446655440001/profile"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "subtitle": "<p>Where work happens<\/p>",
    "overview": "<p>Slack is a messaging platform...<\/p>",
    "pricing": "<p>Free, Pro $7.25\/user<\/p>",
    "parent_category_id": 5,
    "review_platforms": [],
    "videos": [
        "architecto"
    ],
    "categories": [
        []
    ],
    "segments": [
        []
    ],
    "search_fields": [],
    "competitors": [
        []
    ],
    "deal": "architecto",
    "deals_meta_title": "Best Slack Deals 2026",
    "deals_meta_description": "architecto",
    "cancellation_content": "architecto",
    "cancellation_content_summary": "architecto",
    "book_demo_url": "https:\/\/slack.com\/demo",
    "pricing_url": "https:\/\/slack.com\/pricing",
    "pros_cons": "architecto",
    "analysis": "architecto",
    "faq": "architecto",
    "alternatives_text": "architecto",
    "pricing_range": "$99-$999\/mo",
    "is_ai_powered": true,
    "meta": []
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "message": "Change request submitted for review.",
    "data": {
        "change_request_uuid": "880e8400-e29b-41d4-a716-446655440003",
        "status": "pending"
    }
}
 

Example response (404):


{
    "message": "This project does not have a product profile yet."
}
 

Request      

PUT api/projects/{project_uuid}/profile

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

project_uuid   string     

The project UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Body Parameters

subtitle   string  optional    

The product tagline (HTML supported). Example: <p>Where work happens</p>

overview   string  optional    

The product description (HTML supported). Example: <p>Slack is a messaging platform...</p>

pricing   string  optional    

Pricing information (HTML supported). Example: <p>Free, Pro $7.25/user</p>

parent_category_id   integer  optional    

The parent category ID from Curiosity. Example: 5

review_platforms   object  optional    

Review platform URLs and data.

g2   object  optional    
url   string  optional    

G2 review page URL. Example: https://g2.com/products/slack

enabled   boolean  optional    

Whether G2 is enabled. Example: true

capterra   object  optional    
url   string  optional    

Capterra review page URL. Example: https://capterra.com/p/123/slack

enabled   boolean  optional    

Whether Capterra is enabled. Example: true

videos   string[]  optional    

List of video URLs.

categories   object[]  optional    

List of category objects with id and name.

segments   object[]  optional    

List of segment objects with id and name.

search_fields   object  optional    

Search field categorization by type (built_for, platform, pricing_model arrays).

competitors   object[]  optional    

List of competitor products with id, name, url, logo_path.

deal   string  optional    

Deal content/description. Example: architecto

deals_meta_title   string  optional    

SEO meta title for deals page. Example: Best Slack Deals 2026

deals_meta_description   string  optional    

SEO meta description for deals page. Example: architecto

cancellation_content   string  optional    

How to cancel subscription content (HTML supported). Example: architecto

cancellation_content_summary   string  optional    

Summary of cancellation info (HTML supported). Example: architecto

book_demo_url   string  optional    

URL to book a demo. Example: https://slack.com/demo

pricing_url   string  optional    

URL to pricing page. Example: https://slack.com/pricing

pros_cons   string  optional    

Product pros and cons. Example: architecto

analysis   string  optional    

Detailed product analysis (HTML supported). Example: architecto

faq   string  optional    

Frequently asked questions. Example: architecto

alternatives_text   string  optional    

Description of alternatives (HTML supported). Example: architecto

pricing_range   string  optional    

Price tier display. Example: $99-$999/mo

is_ai_powered   boolean  optional    

Whether product uses AI technology. Example: true

meta   object  optional    

SEO meta tags for various pages.

main_page   object  optional    
title   string  optional    

Main page meta title. Example: Slack - Where Work Happens

description   string  optional    

Main page meta description. Example: Eius et animi quos velit et.

deals   object  optional    
title   string  optional    

Deals page meta title. Example: architecto

description   string  optional    

Deals page meta description. Example: Eius et animi quos velit et.

cancellation   object  optional    
title   string  optional    

Cancellation page meta title. Example: architecto

description   string  optional    

Cancellation page meta description. Example: Eius et animi quos velit et.

requires authentication

Upload a new logo image for the product profile. The logo is stored locally and will be synced to Curiosity when you push changes.

Get Sync Status

requires authentication

Get the current sync status of a project's product profile.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/projects/660e8400-e29b-41d4-a716-446655440001/profile/status"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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
        }
    }
}
 

Example response (200):


{
    "data": {
        "has_profile": false,
        "has_curiosity_link": true,
        "sync_status": null,
        "synced_at": null,
        "local_changes_at": null,
        "last_change_request": null
    }
}
 

Request      

GET api/projects/{project_uuid}/profile/status

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

project_uuid   string     

The project UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Catalog

Reference data for product categorization and filtering.

These endpoints provide access to taxonomy data from Curiosity used when editing product profiles.

Available Data:

List Parent Categories

requires authentication

Get all top-level parent categories for product classification. Parent categories represent broad product domains.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/catalog/parent-categories"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "id": 1,
            "name": "Analytics"
        },
        {
            "id": 2,
            "name": "Communication"
        },
        {
            "id": 3,
            "name": "Marketing"
        },
        {
            "id": 4,
            "name": "Project Management"
        },
        {
            "id": 5,
            "name": "Sales"
        }
    ]
}
 

Request      

GET api/catalog/parent-categories

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

List Categories

requires authentication

Get all detailed categories for product classification. Categories are more specific than parent categories and can be assigned to products.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/catalog/categories"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
        }
    ]
}
 

Request      

GET api/catalog/categories

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

List Segments

requires authentication

Get all market segments for product targeting. Segments define the target audience or market size for products.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/catalog/segments"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "id": 1,
            "name": "Enterprise"
        },
        {
            "id": 2,
            "name": "Mid-Market"
        },
        {
            "id": 3,
            "name": "SMB"
        },
        {
            "id": 4,
            "name": "Startup"
        },
        {
            "id": 5,
            "name": "Freelancer"
        }
    ]
}
 

Request      

GET api/catalog/segments

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

List Search Fields

requires authentication

Get all search field options grouped by type. Search fields are structured attributes used for filtering and discovery.

Field Types:

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/catalog/search-fields"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
            }
        ]
    }
}
 

Request      

GET api/catalog/search-fields

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Projects

APIs for managing projects within organisations

List Projects

requires authentication

Get all projects in an organisation that the user has access to.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/b47b1b40-d5f7-4e9c-959a-b7affbc9965c/projects"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
        }
    ]
}
 

Request      

GET api/organisations/{organisation_uuid}/projects

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

Example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c

organisation   string     

The organisation UUID. Example: 550e8400-e29b-41d4-a716-446655440000

Create Project

requires authentication

Create a new project in an organisation. Requires organisation admin role.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/b47b1b40-d5f7-4e9c-959a-b7affbc9965c/projects"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "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": {
        "g2": {
            "enabled": true,
            "url": "https:\/\/g2.com\/products\/acme"
        },
        "capterra": {
            "enabled": false,
            "url": null
        }
    },
    "reddit_keywords": [
        "acme",
        "acme app"
    ],
    "reddit_brand_name": "Acme",
    "social_platform": {
        "twitter": {
            "enabled": true,
            "url": "https:\/\/twitter.com\/acme"
        }
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
    "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"
    }
}
 

Request      

POST api/organisations/{organisation_uuid}/projects

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

Example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c

organisation   string     

The organisation UUID. Example: 550e8400-e29b-41d4-a716-446655440000

Body Parameters

product_name   string     

The product name. Example: Acme App

product_website   string  optional    

The product website URL. Example: https://acme.com

product_logo   string  optional    

The product logo URL. Example: https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID

review_platforms   object  optional    

Review platform settings.

reddit_keywords   string[]  optional    

Reddit keyword variations.

reddit_brand_name   string  optional    

The brand name for Reddit tracking. Example: Acme

social_platform   object  optional    

Social platform configuration.

Get Project

requires authentication

Get details of a specific project.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/b47b1b40-d5f7-4e9c-959a-b7affbc9965c/projects/d94fca2e-1789-491b-88a9-03b5738dd218"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
    }
}
 

Request      

GET api/organisations/{organisation_uuid}/projects/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

Example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c

uuid   string     

Example: d94fca2e-1789-491b-88a9-03b5738dd218

organisation   string     

The organisation UUID. Example: 550e8400-e29b-41d4-a716-446655440000

project   string     

The project UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Update Project

requires authentication

Update a project's details. Requires organisation admin role.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/b47b1b40-d5f7-4e9c-959a-b7affbc9965c/projects/d94fca2e-1789-491b-88a9-03b5738dd218"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "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": {
        "g2": {
            "enabled": true,
            "url": "https:\/\/g2.com\/products\/acme"
        },
        "capterra": {
            "enabled": false,
            "url": null
        }
    },
    "reddit_keywords": [
        "acme",
        "acme pro"
    ],
    "reddit_brand_name": "Acme",
    "social_platform": {
        "twitter": {
            "enabled": true,
            "url": "https:\/\/twitter.com\/acme"
        }
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "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"
    }
}
 

Request      

PUT api/organisations/{organisation_uuid}/projects/{uuid}

PATCH api/organisations/{organisation_uuid}/projects/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

Example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c

uuid   string     

Example: d94fca2e-1789-491b-88a9-03b5738dd218

organisation   string     

The organisation UUID. Example: 550e8400-e29b-41d4-a716-446655440000

project   string     

The project UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Body Parameters

product_name   string     

The product name. Example: Acme App Pro

product_website   string  optional    

The product website URL. Example: https://acme.com

product_logo   string  optional    

The product logo URL. Example: https://cdn.brandfetch.io/acme.com/fallback/lettermark/icon?c=BRANDFETCH_CLIENT_ID

review_platforms   object  optional    

Review platform settings.

reddit_keywords   string[]  optional    

Reddit keyword variations.

reddit_brand_name   string  optional    

The brand name for Reddit tracking. Example: Acme

social_platform   object  optional    

Social platform configuration.

Delete Project

requires authentication

Delete a project. Requires organisation admin role.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/b47b1b40-d5f7-4e9c-959a-b7affbc9965c/projects/d94fca2e-1789-491b-88a9-03b5738dd218"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (200):


{
    "message": "Project deleted successfully."
}
 

Request      

DELETE api/organisations/{organisation_uuid}/projects/{uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

Example: b47b1b40-d5f7-4e9c-959a-b7affbc9965c

uuid   string     

Example: d94fca2e-1789-491b-88a9-03b5738dd218

organisation   string     

The organisation UUID. Example: 550e8400-e29b-41d4-a716-446655440000

project   string     

The project UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Review Views

APIs for managing saved review filter views (organization-scoped).

Get All Review Views

requires authentication

List all saved review views for the authenticated user in a specific organisation. Views are ordered by sort_order and creation date.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/review-views"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
        }
    ]
}
 

Request      

GET api/organisations/{organisation_uuid}/review-views

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organisation UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Create Review View

requires authentication

Create a new saved review view for the authenticated user.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/review-views"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "High Priority Reviews",
    "filters": {
        "platforms": [
            "G2"
        ],
        "rating_buckets": [
            "1.0-1.9"
        ],
        "brands": [
            "uuid-1",
            "uuid-2"
        ],
        "date_range": {
            "type": "preset",
            "value": "last_3_months"
        },
        "date_range_custom": {
            "start": "2026-01-01",
            "end": "2026-01-24"
        },
        "languages": [
            "en",
            "de",
            "es"
        ],
        "read_status": "unread",
        "search": "customer support",
        "sort_by": "platform",
        "sort_direction": "asc"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (201):


{
  "data": {
    "uuid": "e5f6g7h8-...",
    "name": "High Priority Reviews",
    "sort_order": 1,
    "filters": {...},
    "created_at": "2026-01-24T10:00:00.000Z",
    "updated_at": "2026-01-24T10:00:00.000Z"
  }
}
 

Request      

POST api/organisations/{organisation_uuid}/review-views

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organisation UUID. Example: 660e8400-e29b-41d4-a716-446655440001

Body Parameters

name   string     

The name of the view. Must be unique per user/organisation. Example: High Priority Reviews

filters   object     

Filter configuration.

brands   string[]  optional    

Filter by brands (project UUIDs).

platforms   string[]  optional    

Platforms to filter by.

date_range   object  optional    

Date range preset.

type   string  optional    

This field is required when filters.date_range is present. Example: preset

Must be one of:
  • preset
  • custom
  • all_time
value   string  optional    

This field is required when filters.date_range.type is preset. Example: last_3_months

Must be one of:
  • last_14_days
  • last_3_months
  • last_12_months
  • last_3_years
  • all_time
date_range_custom   object  optional    

Custom date range.

start   string  optional    

This field is required when filters.date_range_custom is present. Must be a valid date. Example: 2026-02-03T13:58:49

end   string  optional    

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

rating_buckets   string[]  optional    

Rating buckets.

languages   string[]  optional    

Language filters (ISO 639-1 codes).

read_status   string  optional    

Read status filter. Example: unread

search   string  optional    

Keyword search. Example: customer support

sort_by   string  optional    

Example: platform

Must be one of:
  • review_date
  • rating
  • platform
  • created_at
sort_direction   string  optional    

Example: asc

Must be one of:
  • asc
  • desc

Get Review View

requires authentication

Get a specific saved review view.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/review-views/a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
  "data": {
    "uuid": "a1b2c3d4-...",
    "name": "Critical Reviews",
    "sort_order": 0,
    "filters": {...},
    "created_at": "2026-01-15T10:30:00.000Z",
    "updated_at": "2026-01-20T14:22:00.000Z"
  }
}
 

Request      

GET api/organisations/{organisation_uuid}/review-views/{view_uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organisation UUID. Example: 660e8400-e29b-41d4-a716-446655440001

view_uuid   string     

The view UUID. Example: a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6

Update Review View

requires authentication

Update an existing saved review view. Can update name and/or filters.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/review-views/a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Critical Reviews - Updated",
    "filters": {
        "platforms": [
            "G2"
        ],
        "brands": [
            "a4855dc5-0acb-33c3-b921-f4291f719ca0"
        ],
        "date_range": {
            "type": "all_time",
            "value": "last_3_months"
        },
        "date_range_custom": {
            "start": "2026-02-03T13:58:49",
            "end": "2052-02-27"
        },
        "rating_buckets": [
            "5.0"
        ],
        "languages": [
            "architecto"
        ],
        "read_status": "all",
        "search": "n",
        "sort_by": "created_at",
        "sort_direction": "asc"
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
  "data": {
    "uuid": "a1b2c3d4-...",
    "name": "Critical Reviews - Updated",
    "sort_order": 0,
    "filters": {...},
    "created_at": "2026-01-15T10:30:00.000Z",
    "updated_at": "2026-01-24T11:15:00.000Z"
  }
}
 

Request      

PUT api/organisations/{organisation_uuid}/review-views/{view_uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organisation UUID. Example: 660e8400-e29b-41d4-a716-446655440001

view_uuid   string     

The view UUID. Example: a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6

Body Parameters

name   string  optional    

The name of the view. Example: Critical Reviews - Updated

filters   object  optional    

Filter configuration.

brands   string[]  optional    

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.

platforms   string[]  optional    
Must be one of:
  • 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   object  optional    
type   string  optional    

This field is required when filters.date_range is present. Example: all_time

Must be one of:
  • preset
  • custom
  • all_time
value   string  optional    

This field is required when filters.date_range.type is preset. Example: last_3_months

Must be one of:
  • last_14_days
  • last_3_months
  • last_12_months
  • last_3_years
  • all_time
date_range_custom   object  optional    
start   string  optional    

This field is required when filters.date_range_custom is present. Must be a valid date. Example: 2026-02-03T13:58:49

end   string  optional    

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

rating_buckets   string[]  optional    
Must be one of:
  • 5.0
  • 4.0-4.9
  • 3.0-3.9
  • 2.0-2.9
  • 1.0-1.9
languages   string[]  optional    
read_status   string  optional    

Example: all

Must be one of:
  • all
  • read
  • unread
search   string  optional    

Must not be greater than 500 characters. Example: n

sort_by   string  optional    

Example: created_at

Must be one of:
  • review_date
  • rating
  • platform
  • created_at
sort_direction   string  optional    

Example: asc

Must be one of:
  • asc
  • desc

Delete Review View

requires authentication

Delete a saved review view.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/review-views/a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());

Example response (204):

Empty response
 

Request      

DELETE api/organisations/{organisation_uuid}/review-views/{view_uuid}

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

organisation_uuid   string     

The organisation UUID. Example: 660e8400-e29b-41d4-a716-446655440001

view_uuid   string     

The view UUID. Example: a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6

Slack Integration

APIs for connecting and managing Slack workspace integration

Slack OAuth Callback

Handles the OAuth callback from Slack after user authorization. Exchanges the authorization code for an access token. This endpoint is unauthenticated - uses cached state for auth context.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/integrations/slack/callback"
);

const params = {
    "code": "123456789.abcdef",
    "state": "abc123...",
    "error": "access_denied",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "code": "architecto",
    "state": "ngzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewt",
    "error": "architecto"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (302, Success):


Redirects to /dashboard/notification/overview?slack_connected=true&workspace={name}
 

Example response (302, Error):


Redirects to /dashboard/notification/overview?slack_error={error_code}
 

Example response (302):

Show headers
cache-control: no-cache, private
location: https://subsig-frontend.vercel.app/dashboard/notification/overview?slack_error=architecto
content-type: text/html; charset=utf-8
vary: Origin
 

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="0;url='https://subsig-frontend.vercel.app/dashboard/notification/overview?slack_error=architecto'" />

        <title>Redirecting to https://subsig-frontend.vercel.app/dashboard/notification/overview?slack_error=architecto</title>
    </head>
    <body>
        Redirecting to <a href="https://subsig-frontend.vercel.app/dashboard/notification/overview?slack_error=architecto">https://subsig-frontend.vercel.app/dashboard/notification/overview?slack_error=architecto</a>.
    </body>
</html>
 

Request      

GET api/integrations/slack/callback

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Query Parameters

code   string  optional    

The authorization code from Slack. Example: 123456789.abcdef

state   string  optional    

The state parameter for CSRF protection. Example: abc123...

error   string  optional    

OAuth error if user denied access. Example: access_denied

Body Parameters

code   string  optional    

This field is required when error is not present. Example: architecto

state   string     

Must be 64 characters. Example: ngzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewt

error   string  optional    

Example: architecto

Initiate Slack OAuth

requires authentication

Starts the OAuth flow to connect a Slack workspace. Redirects to Slack's authorization page.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/integrations/slack/connect"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (302, Redirect to Slack):


Redirects to Slack OAuth page
 

Example response (400, Not configured):


{
    "error": "configuration",
    "message": "Slack integration is not configured."
}
 

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
 

{
    "message": "Unauthenticated."
}
 

Example response (403, No organisation):


{
    "message": "Organisation context required."
}
 

Request      

GET api/integrations/slack/connect

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Get Slack Connection Status

requires authentication

Returns the current Slack connection status for the organisation.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/integrations/slack/status"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, Connected):


{
    "connected": true,
    "team_id": "T123456789",
    "team_name": "My Workspace",
    "scopes": "chat:write,channels:read",
    "connected_at": "2025-01-01T12:00:00Z"
}
 

Example response (200, Not connected):


{
    "connected": false
}
 

Example response (200, Invalid token):


{
    "connected": false,
    "error": "token_invalid",
    "message": "Slack connection needs to be re-authorized."
}
 

Example response (403, No organisation):


{
    "message": "Organisation context required."
}
 

Request      

GET api/integrations/slack/status

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Disconnect Slack

requires authentication

Removes the Slack workspace connection for the organisation.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/integrations/slack/disconnect"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());

Example response (200, Success):


{
    "success": true,
    "message": "Slack connection removed."
}
 

Example response (200, Not connected):


{
    "success": true,
    "message": "No Slack connection found."
}
 

Example response (403, No organisation):


{
    "message": "Organisation context required."
}
 

Request      

POST api/integrations/slack/disconnect

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

List Slack Channels

requires authentication

Fetches the list of channels from the connected Slack workspace.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/integrations/slack/channels"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200, Success):


{
    "ok": true,
    "channels": [
        {
            "id": "C123456789",
            "name": "general",
            "is_member": true
        },
        {
            "id": "C987654321",
            "name": "random",
            "is_member": false
        }
    ]
}
 

Example response (401, Not connected):


{
    "ok": false,
    "error": "not_connected",
    "message": "Not connected to Slack."
}
 

Example response (401, Invalid token):


{
    "ok": false,
    "error": "token_invalid",
    "message": "Slack connection needs to be re-authorized."
}
 

Example response (403, No organisation):


{
    "message": "Organisation context required."
}
 

Request      

GET api/integrations/slack/channels

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Subscriptions

APIs for managing subscriptions

List Subscription Plans

requires authentication

Get all available subscription plans.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/subscription-plans"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "data": [
        {
            "id": 1,
            "stripe_price_id": "price_1234567890",
            "name": "Pro Plan",
            "description": "Professional features",
            "amount": 2999,
            "currency": "usd",
            "interval": "month"
        }
    ]
}
 

Request      

GET api/subscription-plans

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Create Checkout Session

requires authentication

Create a Stripe Checkout session for subscribing to a plan.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/subscriptions/checkout"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "plan_id": 1,
    "success_url": "https:\/\/app.example.com\/subscription\/success",
    "cancel_url": "https:\/\/app.example.com\/subscription\/cancel"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());

Example response (200):


{
    "checkout_url": "https://checkout.stripe.com/pay/cs_test_..."
}
 

Example response (403, No organisation access):


{
    "message": "You do not have access to this organisation."
}
 

Example response (404, Plan not found):


{
    "message": "Subscription plan not found."
}
 

Request      

POST api/subscriptions/checkout

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

plan_id   integer     

The subscription plan ID. Example: 1

success_url   string     

The URL to redirect to after successful payment. Example: https://app.example.com/subscription/success

cancel_url   string     

The URL to redirect to if payment is canceled. Example: https://app.example.com/subscription/cancel

Get Current Subscription

requires authentication

Get the current subscription for the organisation.

Example request:
const url = new URL(
    "https://backend-staging.subsig.com/api/subscriptions/current"
);

const headers = {
    "Authorization": "Bearer 1|abc123...",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());

Example response (200):


{
    "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"
            ]
        }
    }
}
 

Example response (403, No organisation access):


{
    "message": "You do not have access to this organisation."
}
 

Example response (404, No subscription):


{
    "message": "No subscription found for this organisation."
}
 

Request      

GET api/subscriptions/current

Headers

Authorization        

Example: Bearer 1|abc123...

Content-Type        

Example: application/json

Accept        

Example: application/json