Introduction
This documentation aims to provide all the information you need to work with our API.
Mail test server: https://backend-api.subsig.com:8025 (HTTP only)
Postman collection: https://backend-api.subsig.com/docs.postman
OpenAPI spec: https://backend-api.subsig.com/docs.openapi
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-api.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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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 (201, Email not verified):
{
"message": "Registration successful. Please check your email to verify your account.",
"redirect_link": "https://frontend.example.com/register?email=john%40example.com",
"needsEmailVerification": true
}
Example response (422, Invalid credentials):
{
"message": "The provided credentials are incorrect.",
"errors": {
"email": [
"The provided credentials are incorrect."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Authenticate with Google
Verify a Google ID token and log in or register based on intent.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/auth/google"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"token": "eyJhbGciOiJSUzI1NiIs...",
"intent": "login",
"appsumo_registration_token": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"token": "1|abc123...",
"user": {
"name": "John Doe",
"email": "john@example.com",
"avatar": "https://lh3.googleusercontent.com/..."
}
}
Example response (401, Invalid token):
{
"message": "Invalid Google token."
}
Example response (401, Google account conflict):
{
"message": "This account is linked to a different Google account."
}
Example response (404, Account not found):
{
"code": "ACCOUNT_NOT_FOUND",
"message": "Account not found.",
"errors": {
"email": [
"No account exists for this Google email."
]
}
}
Example response (422, Account already exists):
{
"code": "ACCOUNT_ALREADY_EXISTS",
"message": "An account with this email already exists. Please log in.",
"errors": {
"email": [
"An account with this email already exists. Please log in."
]
}
}
Example response (422, Validation error):
{
"message": "The token field is required.",
"errors": {
"token": [
"The token field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Authenticate with LinkedIn
Verify a LinkedIn access token and log in or register based on intent.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/auth/linkedin"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"token": "AQV...",
"intent": "login",
"appsumo_registration_token": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"token": "1|abc123...",
"user": {
"name": "John Doe",
"email": "john@example.com",
"avatar": "https://media.licdn.com/..."
}
}
Example response (401, Invalid token):
{
"message": "Invalid LinkedIn token."
}
Example response (401, LinkedIn account conflict):
{
"message": "This account is linked to a different LinkedIn account."
}
Example response (404, Account not found):
{
"code": "ACCOUNT_NOT_FOUND",
"message": "Account not found.",
"errors": {
"email": [
"No account exists for this LinkedIn email."
]
}
}
Example response (422, Account already exists):
{
"code": "ACCOUNT_ALREADY_EXISTS",
"message": "An account with this email already exists. Please log in.",
"errors": {
"email": [
"An account with this email already exists. Please log in."
]
}
}
Example response (422, Validation error):
{
"message": "The token field is required.",
"errors": {
"token": [
"The token field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Change Password
requires authentication
Change password for an authenticated API user and return a fresh token.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/auth/change-password"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"current_password": "SecurePass123!",
"password": "NewSecurePass123!"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"message": "Password updated successfully.",
"token": "1|abc123..."
}
Example response (401, Unauthenticated):
{
"message": "Unauthenticated."
}
Example response (422, Current password is incorrect):
{
"message": "The given data was invalid.",
"errors": {
"current_password": [
"The provided password does not match your current password."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Set Password
requires authentication
Set a password for an authenticated user who signed up via Google or LinkedIn. Plaintext is never required for the previous random placeholder password.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/auth/set-password"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"password": "NewSecurePass123!",
"password_confirmation": "NewSecurePass123!"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"message": "Password set successfully.",
"token": "1|abc123..."
}
Example response (422, Password already set):
{
"message": "A password is already set. Use change-password instead.",
"error": {
"code": "password_already_set"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Complete Profile
requires authentication
Fill in the profile details collected on the "About you" step right after email-only registration (see CreateNewUser::create()). Password is only required the first time -- once hasUsablePassword() is true, omit it and use change-password instead.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/auth/complete-profile"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Jane Doe",
"password": "NewSecurePass123!",
"job_role": "founder_ceo",
"company_name": "Acme Corporation",
"phone": "+14155552671",
"agree_to_terms": true,
"agree_to_marketing": false,
"password_confirmation": "NewSecurePass123!"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"message": "Profile completed successfully.",
"token": "1|abc123...",
"user": {}
}
Example response (422, Password already set):
{
"message": "A password is already set. Use change-password instead.",
"error": {
"code": "password_already_set"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Current User
requires authentication
Get the authenticated user's details including organisation and subscription information.
Example request:
const url = new URL(
"https://backend-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Profile
requires authentication
Update the authenticated user's own profile fields. name is kept in sync as
"{first_name} {last_name}" for every other place in the app that still reads it.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/user"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"first_name": "Jane",
"last_name": "Doe",
"phone": "+1 555 123 4567",
"job_role": "Marketing",
"company_name": "Acme Inc"
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Account
requires authentication
Permanently delete the authenticated user's own account. Any organisation this user is the sole member of is deleted along with it (see UserDeletionService); shared organisations are left intact for the remaining members.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/user"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Traffic
The AI Traffics dashboard's GA4-sourced overview -- see GoogleAnalyticsTrafficReportService for which fields are real (GA4 rollups) vs placeholders pending the bot-crawler pipeline.
Traffic Overview
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/traffic"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"date_from": "architecto",
"date_to": "architecto",
"granularity": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Traffic Pages
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/traffic/pages"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"date_from": "architecto",
"date_to": "architecto",
"providers": [
"architecto"
],
"sort_by": "architecto",
"sort_direction": "architecto",
"per_page": 16,
"page": 16
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Chats
The org-wide "Chats" tab -- every AI response (execution or radar pull) across every prompt, with brand-mention/citation/web-search summary stats and a filterable, paginated, exportable list. Deliberately separate from AiVisibilityPromptAnalyticsController's responses()/ exportResponses(): those are hard-scoped to one prompt and gap-fill against that single prompt's own check cadence (AiVisibilityResponseGapFill) -- a concept that doesn't generalize across an org's many prompts with different frequencies. This controller queries the raw execution/radar rows directly with no gap-fill, and duplicates the small amount of row-mapping/CSV logic it needs rather than pulling in that per-prompt machinery.
Chats — List
requires authentication
Paginated, org-wide list of every AI response (execution or radar pull), newest first.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/chats"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"providers": [
"architecto"
],
"date_from": "architecto",
"date_to": "architecto",
"topic_ids": [
16
],
"brand_ids": [
16
],
"sources": [
"architecto"
],
"search": "architecto",
"page": 16,
"per_page": 16
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Chats — Overview
requires authentication
The 4 header cards (Total chats, Brand mentioned, Answers with web search, Average citations), each with a period-over-period change against the filters' comparison window. brand_ids narrows the cards the same way it narrows the table below (top filter bar). Deliberately still ignores sources/search -- those remain table-only refinements.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/chats/overview"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Chats — Export
requires authentication
CSV of the full filtered set (not just the current page).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/chats/export"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Chats — Filters
requires authentication
Distinct citation domains ever seen across the org's chats (execution + radar), for the "All sources" dropdown. Not date-scoped -- a filter's option list is the full universe of values, not just what the current date range happens to show.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/chats/filters"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Chats — Detail
requires authentication
A single chat, keyed by the same composite id the list/export rows carry ("execution-{id}" or "radar-{id}") -- there's no single backend table a chat lives in, so the id itself says which one to look up.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/chats/execution-42"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Competitor
Competitor tab — brand list with visibility/SOV/position metrics and CRUD.
Competitor overview
requires authentication
Returns all tracked brands (own + competitors) with visibility, share-of-voice, and average-position metrics for the requested date window, plus change deltas vs the preceding window of equal length.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/competitors"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"date_from": "architecto",
"date_to": "architecto",
"provider": "architecto",
"page": 16,
"per_page": 16,
"search": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Add competitor
requires authentication
Either website or social_profiles is required (a competitor with no website can still be tracked via its social profiles, same as the own brand's onboarding modes) -- see CitationClassifier for how social_profiles feeds 'competitor' citation classification.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/competitor-brands"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Acme Corp",
"website": "acme.com",
"social_profiles": [
{
"platform": "instagram",
"url": "https:\/\/instagram.com\/acme"
}
],
"location_scope": "architecto",
"location_ids": [
16
],
"track_globally": false
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update competitor
requires authentication
Either website or social_profiles is required, same rule as Add Competitor -- editing a competitor from website-tracked to social-tracked (or vice versa) is a supported mode switch, not just a field edit.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/competitor-brands/16"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "architecto",
"website": "architecto",
"social_profiles": [
{
"platform": "instagram",
"url": "https:\/\/instagram.com\/acme"
}
],
"location_scope": "architecto",
"location_ids": [
16
],
"track_globally": false
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete competitor
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/competitor-brands/16"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Override a competitor's store match
requires authentication
Lets an org admin correct a wrong auto-match (or manually set/clear one) for one of the organisation's own locations. Always sets is_manual_override, so MatchAiVisibilityCompetitorStoreJob never revisits this (brand, location) pair again -- see its own docblock. Upserts, same shape as the job's own persist() method, since an admin may set a match before the job has ever run for this pair.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/competitor-brands/16/locations/16/store-match"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"status": "matched",
"matched_place_id": "architecto",
"matched_name": "architecto",
"matched_address": "architecto"
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Dashboard
Ticket 7 — the Visibility tab. Reads exclusively from ai_visibility_daily_metrics (Ticket 5),
never live-joins execution tables — see AiVisibilityMetricsAggregateQuery.
Visibility Tab
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/visibility"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"date_from": "2026-06-01",
"date_to": "2026-06-30",
"compare_from": "architecto",
"compare_to": "architecto",
"provider": "openai",
"model": "architecto",
"prompt_id": 16,
"country": "architecto",
"language": "architecto",
"source": "architecto",
"prompt_type": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Platforms Tab
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/platforms"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"date_from": "architecto",
"date_to": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Sentiment Tab
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/sentiment"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"brand": 16,
"per_page": 16
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Export Sentiment Sources
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/sentiment/sources/export"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Citations Tab
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/citations"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Citations — Domains Overview
requires authentication
Cards, retrievals trend, top domains and the domain-type breakdown for the Domains main screen — the domain-scoped counterpart of index()'s combined payload, so that screen isn't pulling URL-scoped fields it never renders.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/citations/domains-overview"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Citations — URLs Overview
requires authentication
URL-scoped counterpart of domainsOverview() above, for the URLs main screen.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/citations/urls-overview"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Citations — Domains
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/citations/domains"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"per_page": 16
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Citations — URLs
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/citations/urls"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"per_page": 16
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Export Citation URLs
requires authentication
Flat, one-row-per-citation-occurrence export matching the format competitor tools like Otterly.ai use -- not the on-screen URL rollup, which aggregates many occurrences of the same URL into one summary row and can't carry per-occurrence fields like Prompt/Date.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/citations/urls/export"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"domain": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Export Citation Domains
requires authentication
Flat, one-row-per-citation-occurrence export matching the format competitor tools like Otterly.ai use -- not the on-screen domain rollup, which aggregates many occurrences of the same domain into one summary row and can't carry per-occurrence fields like Prompt/Date.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/citations/domains/export"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Citations — Domain Detail
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/citations/domain-detail"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"domain": "reddit.com"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Citations — URLs for a Domain (paginated)
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/citations/domain-urls"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"domain": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Citations — URL Detail
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/citations/url-detail"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "http:\/\/www.bailey.biz\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Citations — AI Responses that cited a URL (paginated)
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/citations/url-responses"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "http:\/\/www.bailey.biz\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Export AI Responses that cited a URL
requires authentication
One row per citation occurrence (a citation URL from a specific prompt/service/date), matching the flat format competitor tools like Otterly.ai export -- not one row per response, since a single response can cite several URLs.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/citations/url-responses/export"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "http:\/\/www.bailey.biz\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Fanout Queries
The org-wide "Fanout Query" tab -- every sub-query an AI engine ran behind the scenes to answer a tracked prompt, across both the active LLM pipeline (ai_visibility_fanout_queries) and the radar/google_ai_overviews pipeline (ai_visibility_radar_fanout_queries). See AiVisibilityFanoutQueryAggregateQuery's class doc for exactly which providers ever populate either table and why.
Fanout Queries: List
requires authentication
Paginated, searchable, org-wide list of distinct query fanouts, most-occurring first. Pass topic_id to page through just one topic's group of queries (the "Group by Topics" UI expands one topic at a time and lazy-loads its own page via this same endpoint) -- the topic list itself (with in-scope prompt counts) is served by topics() below.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/fanout"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"providers": [
"architecto"
],
"date_from": "architecto",
"date_to": "architecto",
"search": "architecto",
"type": "architecto",
"topic_id": 16,
"page": 16,
"per_page": 16
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Fanout Queries: Overview
requires authentication
The 4 header cards, each with a period-over-period change against the filters' comparison window.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/fanout/overview"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"providers": [
"architecto"
],
"date_from": "architecto",
"date_to": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Fanout Queries: Topic Groups
requires authentication
Powers the "Group by Topics" accordion -- only topics that actually have a fanout query in the current filtered scope, each with its distinct-prompt count (the "N Prompts" badge). Distinct from GET /ai-visibility/topics (every topic, unscoped prompt-status counts): that endpoint doesn't know about date/provider filters or fanout data at all.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/fanout/topics"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Fanout Queries: Brand Mentioned
requires authentication
Donut-chart-shaped brand breakdown: each tracked brand's share of the executions/radar pulls behind the in-scope fanout queries that mentioned it. Every slice sums to 100%, same invariant as Visibility's share-of-voice donut.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/fanout/brand-mentioned"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Fanout Queries: Common Terms
requires authentication
Most frequent 2-3 word phrases across every fanout query's text in scope.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/fanout/common-terms"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Fanout Queries: Export
requires authentication
CSV of the full filtered, grouped query list (not just the current page).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/fanout/export"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Locations
Settings > Account and Workspaces "Locations" list -- unlike onboarding's Step 1 (where every location is collected client-side and submitted in one batch because the organisation doesn't exist yet), the organisation already exists here, so each location is a real, immediate call.
Visibility by location
requires authentication
One visibility/SOV/rank row per location for the Home "Locations" card -- not a raw location list (see workspaceSummary() for that). Empty for an org with no locations.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/locations"
);
const params = {
"date_from": "architecto",
"date_to": "architecto",
};
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 (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Add a location
requires authentication
The address is resolved client-side first via the existing Google Places lookup endpoints (GooglePlacesLookupController, unchanged) -- this endpoint only persists the already-resolved fields, it never calls the Google Places API itself.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/locations"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Times Square",
"country_code": "US",
"locality": "New York, United States",
"formatted_address": "architecto",
"google_place_id": "architecto",
"outlet_page_url": "http:\/\/www.bailey.biz\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Workspace locations summary
requires authentication
Settings' "Locations" card and the multi-location gate (useWorkspaceLocations): the own
brand's workspace type, every location with its tracked prompt/competitor counts, and the
org-wide Global (no-location) counts. A single workspace always returns an empty
locations array, regardless of how many rows exist (there shouldn't be any).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/locations"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Add a location (frontend contract)
requires authentication
Same persistence path as store(), just the field names the dashboard's "Add location" modal
actually sends (address/city/place_id/outlet_url instead of
formatted_address/locality/google_place_id/outlet_page_url).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/locations"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "architecto",
"country_code": "architecto",
"city": "architecto",
"address": "architecto",
"place_id": "architecto",
"outlet_url": "http:\/\/www.bailey.biz\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Edit a location (frontend contract)
requires authentication
No equivalent existed before this -- Phase 1 only ever shipped create/list. Same field rename as storeForFrontend().
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/locations/16"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "b",
"country_code": "ng",
"city": "z",
"address": "m",
"place_id": "i",
"outlet_url": "http:\/\/www.okon.com\/accusantium-harum-mollitia-modi-deserunt-aut-ab"
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Prompt Detail
Ticket 7 — the prompt-detail scrollable page (overview/citations/responses) and the top-level execution detail view. Kept separate from AiVisibilityPromptController (already ~1700 lines, and owns a different, older payload shape via show()/executionShow()) — these are new endpoints with their own contract, built on the same Ticket 5 aggregate-table services as the rest of Ticket 7 rather than that controller's live-join helpers.
Prompt Overview
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/overview"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Prompt Filter Options
requires authentication
Distinct providers, models, countries and languages that have actually run for this prompt. Used to populate the filter dropdowns on the prompt detail page.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/filters"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Prompt Citations
requires authentication
Same shape as POST /ai-visibility/citations, scoped to this prompt.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/citations"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Prompt Citation Domains (paginated)
requires authentication
Same shape as POST /ai-visibility/citations/domains, scoped to this prompt.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/citations/domains"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"per_page": 16,
"page": 16
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Prompt Citation URLs (paginated)
requires authentication
Same shape as POST /ai-visibility/citations/urls, scoped to this prompt.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/citations/urls"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"per_page": 16,
"page": 16
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Export Prompt Citation URLs
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/citations/export"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/organisations/{organisation_uuid}/ai-visibility/prompts/{prompt}/responses
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/ai-visibility/prompts/architecto/responses"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Prompt Response Detail
requires authentication
Full detail for a single execution — backs the "AI Response" modal on the prompt responses tab.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/responses/42"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"execution_id": 42,
"prompt_title": "How does Notion help teams?",
"provider": "openai",
"model": "gpt-4o",
"date": "2026-06-25 09:00:00",
"detected_brands": [
{
"name": "Notion",
"website": "notion.com"
}
],
"sentiment": "positive",
"is_brand_mentioned": true,
"response_content": "Notion helps teams...",
"citations": [
{
"rank": 1,
"url": "https://notion.so/blog",
"title": "Notion Blog",
"domain": "notion.so",
"page_type": "references"
}
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Full detail for a single radar (Google AI Overview) response.
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/prompts/16/radar-responses/16"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Export Prompt Responses
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/responses/export"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Execution Detail
requires authentication
Not scoped to a prompt and exempt from the dashboard cache — reads live, per Ticket 7's spec. Full detail (including inline citation segments) is available regardless of whether raw_response has since been pruned by retention (Ticket 8) — citation_segments is derived once at normalization time (see NormalizeAiVisibilityExecutionJob) and persisted alongside generated_answer, neither of which the prune command ever touches.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/executions/501"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Prompts
APIs for managing AI Visibility prompts, their scheduled executions, and execution detail.
List Prompts
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts"
);
const params = {
"topic_id": "16",
"status": "active",
"search": "pricing",
"per_page": "20",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create Prompt
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"text": "How does our product compare to competitors?",
"topic_id": 16,
"tag_ids": [
16
],
"country": "NG",
"language": "en",
"frequency": "weekly",
"providers": [
"openai",
"perplexity"
],
"skip_first_run": true,
"location_scope": "architecto",
"location_ids": [
16
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"id": 1,
"text": "How does our product compare to competitors?",
"topic_id": 16,
"country": "NG",
"language": "en",
"frequency": "weekly",
"status": "active",
"providers": [
"openai",
"perplexity"
]
}
}
Example response (403):
{
"message": "AI Visibility is not enabled for this organisation."
}
Example response (422):
{
"message": "Active prompt limit reached for this plan."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Bulk Import Prompts
requires authentication
Accepts a flat list of prompt texts assigned to a single topic, split against the plan active-prompt limit: prompts that fit are created as active and dispatched on the priority queue; any overflow is created as paused. Case-insensitive exact duplicates are skipped.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/prompts/bulk-import"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"topic_id": 16,
"prompts": [
"architecto"
],
"activate": false,
"topics": [
"g"
],
"tags": [
[
"z"
]
],
"locations": [
{
"scope": "architecto",
"location_ids": [
16
]
}
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Bulk Delete Prompts
requires authentication
Deletes multiple tracked prompts at once, purging each one's historical visibility/ sentiment/citation contribution the same way a single delete does (see AiVisibilityPromptDataPurger). An ID that doesn't belong to this organisation is silently skipped rather than failing the whole batch -- same "unowned resource is invisible, not an error" posture as findPromptOrFail()'s 404 on the single-delete endpoint, just non-fatal here since a bulk selection can't assume every ID is still valid by the time it's submitted.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/prompts/bulk-delete"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"prompt_ids": [
16
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Organisation-Level Execution Status
requires authentication
Polling endpoint for the frontend to show a banner ("results are being recalculated") while any AI Visibility execution across the organisation is still in progress. Bounded by updated_at (not scheduled_at -- recurring prompts get scheduled_at pinned to midnight, so that column can't tell "just went pending/running" apart from "queued 10 hours ago") to a recent window rather than all-time, since any legitimate in-flight execution resolves (completes, fails, or gets reaped by ai-visibility:reap-stale-executions) well within it. Also counts radar (Google AI Overview) rows still in 'pending' status.
in_progress_count is the number of distinct prompts still in flight, not the number of rows -- a single prompt fans out to one execution row per provider (openai, perplexity, ...) plus its own radar row, so counting rows would overstate how many prompts are actually still running.
Always queried fresh (no caching layer, no-store response header) — every poll reflects the current DB state exactly.
reprocessing is a second, independent signal folded into the same in_progress flag: true
while a brand/competitor/workspace rename's ReprocessAiVisibilityOrganisationJob fan-out is
still working through this org's historical executions/radar responses (see
AiVisibilityReprocessingTracker). Surfaced separately from in_progress_count since it isn't
a count of prompts in flight -- the frontend banner shows different copy for it.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/execution-status"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"in_progress": true,
"in_progress_count": 2,
"reprocessing": false,
"in_progress_prompt_ids": [
"101",
"104"
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Regions In Use
requires authentication
Distinct region/country codes actually configured on this organisation's own prompts -- NOT the full curated config('ai_visibility.supported_regions') list that OrganisationController::supportedRegions() returns (that one backs region pickers on creation forms, where every supported region is a valid choice). This backs the AI Visibility dashboard's region filter dropdowns (Platforms/Visibility/Sentiment/ Citations), which should only ever offer a region that at least one of this org's prompts could actually produce data for -- offering the full global list there let an org filter by a region with zero prompts and see an always-empty result with no indication why.
Scoped to active/paused prompts only, same default scope as index() -- an archived prompt's region isn't a live filtering concern.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/regions"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"value": "US",
"label": "United States"
},
{
"value": "GB",
"label": "United Kingdom"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Prompt
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Example response (404):
{
"message": "Prompt not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Prompt
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"text": "How does our product compare to competitors?",
"topic_id": 16,
"tag_ids": [
16
],
"country": "NG",
"language": "en",
"frequency": "weekly",
"status": "active",
"providers": [
"openai",
"perplexity"
]
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (404):
{
"message": "Prompt not found."
}
Example response (422):
{
"message": "This frequency is not allowed on the current plan."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Duplicate Prompt
requires authentication
Creates a paused copy of the prompt with the same topic, tags, text, country, language, frequency and providers.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/duplicate"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (404):
{
"message": "Prompt not found."
}
Example response (422):
{
"message": "Active prompt limit reached for this plan."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Prompt
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204):
Empty response
Example response (404):
{
"message": "Prompt not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Run Prompt Now
requires authentication
Manually dispatches a fresh execution for this prompt across all of its allowed providers, independent of the scheduler.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/run"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (202):
{
"data": {
"prompt_id": 1,
"execution_ids": [
501,
502
]
}
}
Example response (404):
{
"message": "Prompt not found."
}
Example response (422):
{
"message": "No allowed and available provider could run this prompt."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List Prompt Executions
requires authentication
"All Responses" list — one row per execution ("chat") run for this prompt, newest first.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/executions"
);
const params = {
"provider": "openai",
"status": "completed",
"per_page": "20",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Example response (404):
{
"message": "Prompt not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Prompt Execution Detail
requires authentication
"Edit Responses" chat detail — full generated answer, citations, and per-brand mentions for a single execution.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/executions/501"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Example response (404):
{
"message": "Execution not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Prompt Execution Status
requires authentication
Polling endpoint for the frontend to show a loading skeleton while a prompt's latest run is still in progress. "In progress" covers both an execution that hasn't finished calling its provider yet, and one that has (status=completed) but whose citations/brand-mentions haven't been written yet by the normalization job that runs right after — status alone flips to completed before that data exists, so this checks processing_version too rather than reporting "ready" before there's anything real to show. Also folds in the org's latest radar (Google AI Overview) pull for this prompt, if radar is enabled on its plan — radar rows live in a separate table (ai_visibility_radar_responses) with their own pending/normalised/failed lifecycle, but ai_visibility_radar_responses.prompt_id is a real FK to this same prompt, so it belongs in this prompt's status too.
Always queried fresh (no caching layer, no-store response header) — every poll reflects the current DB state exactly.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/1/execution-status"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"status": "in_progress",
"scheduled_at": "2026-08-14 12:23:00",
"providers": [
{
"provider": "openai",
"status": "in_progress",
"location_id": null
},
{
"provider": "perplexity",
"status": "ready",
"location_id": null
},
{
"provider": "google_ai_overviews",
"status": "ready",
"location_id": null
}
]
}
}
Example response (404):
{
"message": "Prompt not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Saved Views
Named filter-state snapshots for the AI Visibility Overview page (date range/providers/brands/ topics/tags/regions/prompt_type) -- "Weekly review", "US market", etc. Shared across the organisation: any member can save, see, and delete a view, same as tags/topics. There is no persisted "Default" row -- that's the frontend's own reset state, never saved here.
List Saved Views
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/saved-views"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": 1,
"name": "Weekly review",
"filters": {
"date_preset": "last_30_days",
"providers": [],
"brand_ids": [],
"topic_ids": [],
"tag_ids": [],
"regions": [],
"prompt_type": null
},
"created_at": "2026-10-08T10:00:00.000000Z",
"updated_at": "2026-10-08T10:00:00.000000Z"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Save Current View
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/saved-views"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Weekly review",
"filters": []
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"id": 1,
"name": "Weekly review",
"filters": {
"date_preset": "last_30_days",
"providers": [],
"brand_ids": [],
"topic_ids": [],
"tag_ids": [],
"regions": [],
"prompt_type": null
},
"created_at": "2026-10-08T10:00:00.000000Z",
"updated_at": "2026-10-08T10:00:00.000000Z"
}
}
Example response (422):
{
"message": "The name field is required. (and 1 more error)",
"errors": {
"name": [
"\"Weekly review\" already exists. Pick another name or use the existing view."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Saved View
requires authentication
Owner-only -- unlike creating a view (Owner or Member), deleting one is restricted to organisation owners, regardless of who created it. See OrganisationPolicy::deleteAiVisibilitySavedView().
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/saved-views/1"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204):
Empty response
Example response (403):
{
"message": "You must be an organisation owner to delete a saved view."
}
Example response (404):
{
"message": "Saved view not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Suggestions
Prompt and topic suggestions generated by GenerateAiVisibilitySuggestionsJob. Suggestions start
as completed and move to accepted or dismissed when the user acts on them.
List prompt suggestions
requires authentication
Returns every prompt suggestion with status completed for the organisation, ordered by
source (radar first), then volume descending, then most recent. This backs the full
"Suggested" tab (search + accept/dismiss over the whole set), so it is not capped the way
the onboarding preview endpoints are -- meta.total always equals count(data).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/suggested"
);
const params = {
"exclude_categories[0]": "branded",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": 1,
"text": "What is the best tool for notes and docs?",
"category": "awareness",
"source": "radar",
"volume": 12000,
"status": "completed",
"generated_at": "2026-06-01 00:00:00"
}
],
"meta": {
"total": 1
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Refetch suggestions
requires authentication
A separate, standalone generation path from GenerateAiVisibilitySuggestionsJob (the
onboarding job) — deliberately not shared code, so this can never change that job's
behavior. Two differences from onboarding generation: (1) previously dismissed suggestions
are deleted first so a fresh batch can take their place — an upsert alone would otherwise
leave a dismissed row's regenerated duplicate stuck as dismissed forever, since the upsert
never touches status; completed/accepted suggestions are left untouched. (2) an LLM
failure here is a real error, not silently swallowed into the generic fallback set — a user
who clicks "refetch" wants real, freshly-generated suggestions, and substituting the generic
fallback would look like a genuine refresh when it isn't one.
Rate-limited to once per 10 minutes per org: a successful call holds its lock for the full
cooldown (not released in a finally) so a second click can't trigger another LLM call too
soon; a failed call releases immediately since it never produced anything worth protecting.
A request that lands inside an active cooldown isn't rejected — it gets back the same
existing (still-valid) suggestions with cached: true, rather than a bare error.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/suggested/refetch"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"topics": [
{
"id": 1,
"name": "Feature Discovery",
"description": "...",
"source": "llm",
"status": "completed",
"generated_at": "2026-06-01 00:00:00"
}
],
"prompts": [
{
"id": 1,
"text": "What is the best tool for notes and docs?",
"category": "awareness",
"source": "llm",
"status": "completed",
"generated_at": "2026-06-01 00:00:00"
}
]
}
}
Example response (200):
{
"data": {
"topics": [],
"prompts": []
},
"cached": true
}
Example response (422):
{
"message": "No own brand found for this organisation."
}
Example response (502):
{
"message": "Failed to generate suggestions. Please try again."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Accept a prompt suggestion
requires authentication
Creates a live prompt from the suggestion, dispatches an immediate first run, and marks the
suggestion as accepted. Returns the new prompt's ID and settings.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/suggested/1/accept"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"frequency": "daily",
"providers": [
"openai"
],
"topic_id": 3,
"country": "US",
"language": "en",
"skip_first_run": false,
"location_scope": "architecto",
"location_ids": [
16
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"prompt_id": 42,
"text": "What is the best tool for notes and docs?",
"topic_id": 3,
"frequency": "daily",
"providers": [
"openai"
],
"country": "US",
"language": "en"
}
}
Example response (422):
{
"message": "Active prompt limit reached for this plan."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Dismiss a prompt suggestion
requires authentication
Marks the suggestion as dismissed. It will no longer appear in the suggestions list.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/prompts/suggested/1"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204):
Empty response
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Onboarding suggestions
requires authentication
Returns both topic and prompt suggestions for the onboarding wizard in a single call.
Dispatch GenerateAiVisibilitySuggestionsJob for the org before calling this endpoint to
ensure suggestions are populated.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/onboarding/suggestions"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"topics": [
{
"id": 1,
"name": "Feature Discovery",
"description": "...",
"category": "feature",
"source": "llm",
"status": "completed",
"generated_at": "2026-06-01 00:00:00"
}
],
"prompts": [
{
"id": 1,
"text": "What is the best tool for notes?",
"category": "awareness",
"source": "radar",
"volume": 12000,
"status": "completed",
"generated_at": "2026-06-01 00:00:00"
}
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Onboarding kickoff
requires authentication
Dispatches first-run executions on the priority queue for all active prompts the org accepted during onboarding (those created with skip_first_run=true). Call this from the BrandAnalysisProgress loading screen, after the user has committed to onboarding (step 4), so we don't waste compute on users who abandon after the suggestion step.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/onboarding/kickoff"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (202):
{
"data": {
"dispatched": 5
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Onboarding analysis status
requires authentication
Returns how many of the org's active prompt executions have completed and a live visibility percentage computed directly from execution_brands rows (no daily_metrics aggregation needed — available as soon as the first execution is normalised).
Poll this from BrandAnalysisProgress until ready is true.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/onboarding/status"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"ready": true,
"executions_total": 10,
"executions_completed": 8,
"visibility_pct": 45
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Onboarding improvement estimate
requires authentication
One-shot, synchronous LLM call — no queue, no polling. Call this once from the onboarding screen right when onboardingStatus() first reports has_own_brand_data (i.e. the real, aggregated visibility_pct is already showing), so the estimate stays grounded in the exact same number the dashboard will show after login. Cached for 30 days, so a repeat call (re-entering onboarding, a second tab) returns instantly without re-running the LLM. Returns potential_visibility_pct: null if there isn't enough own-brand data yet, or if the estimate genuinely can't be computed (LLM call failed, no headroom found).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/onboarding/improvement"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200):
{
"data": {
"potential_visibility_pct": 45
}
}
Example response (200):
{
"data": {
"potential_visibility_pct": null
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List suggested competitors
requires authentication
Returns up to 10 pending competitor suggestions for the organisation, most recent first. Populated by SuggestAiVisibilityCompetitorsJob (dispatched at onboarding and daily thereafter via ai-visibility:suggest-competitors).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/competitors/suggested"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": 1,
"name": "Acme Corp",
"website": "acme.com",
"reason": "Direct competitor in the same market.",
"generated_at": "2026-07-09 00:00:00"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Accept a suggested competitor
requires authentication
Creates a real, tracked competitor brand from the suggestion (source=manual, same as one added directly via the Competitor tab), backfills its historical mention data across every completed execution so far, and marks the suggestion accepted.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/competitors/suggested/1/accept"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"location_scope": "architecto",
"location_ids": [
16
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"brand_id": 42,
"name": "Acme Corp",
"website": "acme.com"
}
}
Example response (422):
{
"message": "This suggestion has already been accepted."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Dismiss a suggested competitor
requires authentication
Marks the competitor suggestion as rejected. It will no longer appear in the suggestions
list, and the name will never be suggested again for this organisation.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/competitors/suggested/1"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204):
Empty response
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List topic suggestions
requires authentication
Returns up to 10 topic suggestions with status completed for the organisation,
ordered by most recent first.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/topics/suggested"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": 1,
"name": "Feature Discovery",
"description": "Questions about discovering features in productivity tools",
"category": "feature",
"source": "llm",
"status": "completed",
"generated_at": "2026-06-01 00:00:00"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Accept a topic suggestion
requires authentication
Creates a real topic from the suggestion and marks it as accepted.
Returns the new topic's ID and details.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/topics/suggested/1/accept"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (201):
{
"data": {
"topic_id": 5,
"name": "Feature Discovery",
"description": "Questions about discovering features in productivity tools"
}
}
Example response (422):
{
"message": "This suggestion has already been accepted."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Dismiss a topic suggestion
requires authentication
Marks the topic suggestion as dismissed. It will no longer appear in the suggestions list.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/topics/suggested/1"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204):
Empty response
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility - Tags
APIs for managing tags used to group AI Visibility prompts within an organisation. Unlike ai_visibility_topics (one nullable topic per prompt), a prompt can carry several tags -- see ai_visibility_prompt_tags.
List Tags
requires authentication
Create Tag
requires authentication
Update Tag
requires authentication
Delete Tag
requires authentication
Removes the tag from every prompt it's attached to (ai_visibility_prompt_tags cascades) -- the prompts themselves, and their execution history/results, are untouched. Unlike AiVisibilityTopicController::destroy(), this is never blocked by active prompts: a tag is a loose label, not the single required grouping a topic is.
AI Visibility - Topics
APIs for managing topics used to group AI Visibility prompts within an organisation.
List Topics
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/topics"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": 1,
"name": "Competitor comparisons",
"description": "Prompts comparing us against competitors",
"created_at": "2026-06-05T10:00:00.000000Z",
"updated_at": "2026-06-05T10:00:00.000000Z",
"active_count": 3,
"inactive_count": 1,
"suggested_count": 0
},
{
"id": null,
"name": "Unassigned",
"description": null,
"created_at": null,
"updated_at": null,
"active_count": 2,
"inactive_count": 0,
"suggested_count": 0
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create Topic
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/topics"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Competitor comparisons",
"description": "Prompts comparing us against competitors"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"id": 1,
"name": "Competitor comparisons",
"description": "Prompts comparing us against competitors",
"created_at": "2026-06-05T10:00:00.000000Z",
"updated_at": "2026-06-05T10:00:00.000000Z"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Topic
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/topics/1"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Competitor comparisons",
"description": "Prompts comparing us against competitors"
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": 1,
"name": "Competitor comparisons",
"description": "Prompts comparing us against competitors",
"created_at": "2026-06-05T10:00:00.000000Z",
"updated_at": "2026-06-05T10:00:00.000000Z"
}
}
Example response (404):
{
"message": "Topic not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Topic
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/660e8400-e29b-41d4-a716-446655440001/ai-visibility/topics/1"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204):
Empty response
Example response (404):
{
"message": "Topic not found."
}
Example response (422):
{
"message": "Cannot delete a topic with active prompts assigned to it."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Visibility — Addon Access
GET /organisations/{organisation_uuid}/ai-visibility/addon-access
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/ai-visibility/addon-access"
);
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):
{
"data": {
"enabled_addon_kinds": [
"grok"
],
"purchased_addon_kinds": [
"grok",
"gemini"
],
"owned_addon_kinds": []
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST /organisations/{organisation_uuid}/ai-visibility/addon-access
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/ai-visibility/addon-access"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"kind": "architecto",
"enabled": false
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, success):
{
"data": {
"enabled_addon_kinds": [
"grok"
],
"purchased_addon_kinds": [
"grok",
"gemini"
],
"owned_addon_kinds": []
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
API Keys
APIs for managing production API keys for the current organisation's billing group. A key is always generated from (and owned by) the group's billing-anchor organisation, but each key is independently scoped to whichever subset of the group's organisations (workspaces) was chosen when it was created -- a billing group can hold several keys, each covering a different subset, for different integrations.
List every production API key generated for this billing group.
requires authentication
Returns every non-revoked key generated from the billing-anchor organisation of whichever organisation was selected via X-Organisation-Id, each with the workspaces it's scoped to. Plaintext API keys are never returned from this endpoint.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/api-keys"
);
const headers = {
"Authorization": "Bearer {token}",
"X-Organisation-Id": "string required The organisation UUID.",
"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",
"environment": "live",
"status": "active",
"key": null,
"masked_key": "sk_live_****************************Ab12",
"last_four": "Ab12",
"plain_text_available": false,
"idempotent_replay": false,
"rotation_expires_at": null,
"expires_at": null,
"revoked_at": null,
"organisations": [
{
"uuid": "660e8400-e29b-41d4-a716-446655440000",
"name": "Acme Inc",
"is_primary": true
}
],
"created_at": "2026-06-05T10:00:00.000000Z",
"updated_at": "2026-06-05T10:00:00.000000Z"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Generate a production API key scoped to a chosen subset of this billing group's organisations. Plaintext is returned only on first creation.
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/api-keys/generate"
);
const headers = {
"Authorization": "Bearer {token}",
"X-Organisation-Id": "string required The organisation UUID.",
"Idempotency-Key": "string optional Safe retry key for this generation request.",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Claude Desktop MCP",
"confirmed": true,
"organisation_uuids": [
"architecto"
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"message": "API key generated successfully.",
"data": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"environment": "live",
"status": "active",
"key": "sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"masked_key": "sk_live_****************************Ab12",
"last_four": "Ab12",
"plain_text_available": true,
"idempotent_replay": false,
"organisations": [
{
"uuid": "660e8400-e29b-41d4-a716-446655440000",
"name": "Acme Inc",
"is_primary": true
}
],
"created_at": "2026-06-05T10:00:00.000000Z",
"updated_at": "2026-06-05T10:00:00.000000Z"
}
}
Example response (422, Workspace outside this billing group):
{
"message": "One or more selected workspaces are not part of this account's billing group.",
"error": {
"code": "organisation_not_in_billing_scope"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Rotate one production API key. Plaintext is returned only on first rotation. The new key keeps the exact same workspace scope as the key it replaces.
requires authentication
Creates a new active production key immediately and keeps the previous key valid for 24 hours before it is auto-revoked.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/api-keys/36010381-2db5-4553-8459-ec8e089eb331/rotate"
);
const headers = {
"Authorization": "Bearer {token}",
"X-Organisation-Id": "string required The organisation UUID.",
"Idempotency-Key": "string optional Safe retry key for this rotation request.",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (201):
{
"message": "API key rotated successfully.",
"data": {
"current_key": {
"uuid": "660e8400-e29b-41d4-a716-446655440000",
"environment": "live",
"status": "active",
"key": "sk_live_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"masked_key": "sk_live_****************************Cd34",
"last_four": "Cd34",
"plain_text_available": true,
"idempotent_replay": false
},
"previous_key": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"environment": "live",
"status": "rotating",
"key": null,
"masked_key": "sk_live_****************************Ab12",
"last_four": "Ab12",
"plain_text_available": false,
"idempotent_replay": false,
"rotation_expires_at": "2026-06-06T10:00:00.000000Z"
}
}
}
Example response (409, Idempotency key conflict):
{
"message": "This idempotency key was already used for a different API key action.",
"error": {
"code": "idempotency_key_conflict"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Revoke one production API key immediately (and its still-rotating predecessor, if any).
requires authentication
The request must include the exact confirmation text REVOKE.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/api-keys/36010381-2db5-4553-8459-ec8e089eb331/revoke"
);
const headers = {
"Authorization": "Bearer {token}",
"X-Organisation-Id": "string required The organisation UUID.",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"confirmation": "REVOKE"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"message": "API key revoked successfully.",
"data": {
"revoked_count": 1
}
}
Example response (422, Missing confirmation):
{
"message": "The selected confirmation is invalid.",
"errors": {
"confirmation": [
"The selected confirmation is invalid."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Replace one production API key's workspace scope entirely -- adds any newly-listed workspace and drops any workspace no longer listed, in one call. Does not affect the key itself (its value, status, or expiry).
requires authentication
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/api-keys/36010381-2db5-4553-8459-ec8e089eb331/workspaces"
);
const headers = {
"Authorization": "Bearer {token}",
"X-Organisation-Id": "string required The organisation UUID.",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"organisation_uuids": [
"architecto"
]
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"message": "API key workspaces updated successfully.",
"data": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"organisations": [
{
"uuid": "660e8400-e29b-41d4-a716-446655440000",
"name": "Acme Inc",
"is_primary": true
},
{
"uuid": "770e8400-e29b-41d4-a716-446655440000",
"name": "Acme Support",
"is_primary": false
}
]
}
}
Example response (422, Workspace outside this billing group):
{
"message": "One or more selected workspaces are not part of this account's billing group.",
"error": {
"code": "organisation_not_in_billing_scope"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AppSumo
APIs for AppSumo lifetime-deal tier comparison and license redemption
Read-only preview so the FE can show "you're redeeming Tier 2 -- here's what you get" before the user commits. No writes.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/appsumo/licenses/validate"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"license_key": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/appsumo/licenses/redeem
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/appsumo/licenses/redeem"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"license_key": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/appsumo/licenses/current
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/appsumo/licenses/current"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List AppSumo tiers
Unauthenticated tier comparison listing (name/price/reviews/mentions/credits per tier) for a marketing/comparison page -- unlike GET /subscription-plans, deliberately does not filter on is_active, since every AppSumo tier is seeded with is_active=false (see AppSumoPlanSeeder) precisely so it's excluded from that general, checkout-oriented listing. Ordered tier 1 -> 4 by config('billing.appsumo_tier_price_ids') key order, not by id.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/appsumo/plans"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"tier": 1,
"stripe_price_id": "price_internal_appsumo_tier1",
"name": "AppSumo Lifetime - Tier 1",
"description": "AppSumo lifetime deal, Tier 1 - 1,200 reviews, 1,000 mentions, 10 tracked prompts, 240 lifetime AI credits",
"amount": 4900,
"currency": "usd",
"plan_threshold": {}
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Endpoints
Send a reset link to the given user.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/auth/forgot-password"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Reset the user's password.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/auth/reset-password"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"token": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Bulk Deactivate Brands (Temporary)
Disabled unless TEMP_BULK_BRAND_DEACTIVATE_ENABLED is set. When TEMP_BULK_BRAND_DEACTIVATE_TOKEN is set, the request must include a matching X-Temporary-Bulk-Token header.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/brands/deactivate-bulk-temporary"
);
const headers = {
"X-Temporary-Bulk-Token": "{token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"chunk_size": 1,
"dry_run": false
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Send a test webhook (manual trigger).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/outbound-webhooks/architecto/test"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List outbound webhooks for the current organisation.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/outbound-webhooks"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create an outbound webhook.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/outbound-webhooks"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "http:\/\/www.bailey.biz\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html",
"headers": [
"i"
],
"auth_type": "bearer",
"auth_token": "k",
"secret": "h",
"is_active": false
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Show a single outbound webhook.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/outbound-webhooks/architecto"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update an outbound webhook.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/outbound-webhooks/architecto"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "http:\/\/www.bailey.biz\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html",
"headers": [
"i"
],
"auth_type": "bearer",
"auth_token": "k",
"secret": "h",
"is_active": false
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete an outbound webhook.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/outbound-webhooks/architecto"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Lets the frontend resolve the org's own tracked brand_id on its own -- every agent endpoint requires it (see AgentRegistry::baseInputSchema()), but there's exactly one is_own_brand row per org and nothing today returns just that id without also computing a full competitor/metrics payload (AiVisibilityCompetitorController::index).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/own-brand"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Populates the run form's "Try our AI Recommendations" chips with real, org-specific observations (see AgentSuggestedPromptGenerator) instead of the agent's static suggestedPrompts() copy. Meaningful for root_cause_analysis and competitive_benchmark -- other agents fall back to their registry-declared suggestedPrompts() unchanged since nothing generates dynamic ones for them yet.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/architecto/suggested-prompts"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"brand_id": 16,
"timeframe": "7d",
"data_source": [
"google_ai_mode"
]
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/organisations/{organisation_uuid}/agents/{key}/runs
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/architecto/runs"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Ticket 5's Recent Outputs listing endpoint -- never built alongside show()/download(), so that page has been stuck on hardcoded demo rows. Matches the mockup's columns exactly: Focus Topics, Agents, Status, Date created. Filterable by agent_type/status per Ticket 5's definition of done; paginated the same way AiVisibilityCompetitorController::index() is.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agent-runs"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Pulled forward from Ticket 5's full listing/polling scope -- the Root Cause Analysis run page needs to poll run_plan step-by-step progress and read result once completed, and nothing else in Ticket 5 (agent listing, Recent Outputs table) is needed for that yet.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agent-runs/17"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Ticket 4/5's own definition of done requires this endpoint, but it was never actually built alongside RenderAgentReportPdfJob -- the PDF existed with nothing to fetch it through. Never returns a permanent URL, only a signed one generated on demand.
Backs both the frontend's Download button (open-and-forget) and Share button (copy the link to hand to someone without a Subsig login) -- one URL serves both, so the expiry has to be long enough for a genuinely shared link to still work hours later, not just a quick click. 24h, matching this feature's other "good enough, not permanent" TTLs (see AgentSuggestedPromptGenerator's suggestion cache).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agent-runs/17/download"
);
const params = {
"format": "docx",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Radar Usage
Returns this month's SerpApi spend for the organisation's passive brand radar, broken down against the plan budget cap (null when uncapped).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/6ff8f7f6-1eb3-3525-be4a-3932c805afed/ai-visibility/radar/usage"
);
const headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Exchange the single-use `code` AppSumo redirects buyers with (after "Activate now") for a license_key + status. Unauthenticated -- a brand-new buyer has no subsig session yet.
AppSumo's own status field flips to "active" as soon as our webhook responds 200 to their
activate event -- which happens independently of whether this specific person has ever
been through our OAuth flow (AppSumoWebhookController always 200s that event). So it does
NOT mean "this browser's user already has a subsig account" -- every first-time buyer would
incorrectly see "active" before ever signing up. already_redeemed is the real signal:
whether our own AppSumoLicense record has an organisation attached yet.
Frontend branches on already_redeemed for new-vs-returning, and on status === "deactivated"
(still AppSumo's own billing-state field, legitimately authoritative for this one case) to
block a cancelled/refunded license outright regardless of redemption state.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/appsumo/oauth/exchange"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"code": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/webhook
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/webhook"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/workspaces
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/workspaces"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 59236191-44ef-4ab7-b2c7-acae8c37d895
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "59236191-44ef-4ab7-b2c7-acae8c37d895"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/ai-visibility/topics
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/topics"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 32d32f0d-b949-47a7-b4ee-706454e30a96
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "32d32f0d-b949-47a7-b4ee-706454e30a96"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/ai-visibility/prompts
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/prompts"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: fb5734a9-b59c-444f-9624-0e1ce910644b
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "fb5734a9-b59c-444f-9624-0e1ce910644b"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/ai-visibility/prompts/{prompt}
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/prompts/564"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 9603b0bc-dbc0-4841-8b72-6ea70e36af5c
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "9603b0bc-dbc0-4841-8b72-6ea70e36af5c"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Every brand tracked by AI Visibility (own brand + registered competitors), ranked, with visibility %/share-of-voice %/average position for each — the discovery step before digging into a specific brand via show() or into sentiment/citations for it. Built on the same AiVisibilityBrandRankService::brandMetrics()/AiVisibilityRanker::rankComposite() combination the session-auth AiVisibilityCompetitorController::index() (Competitor tab) already uses.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/brands"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 93ef386b-6c04-42c5-a1d6-7308b6e284a8
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "93ef386b-6c04-42c5-a1d6-7308b6e284a8"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/ai-visibility/overview
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/overview"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 7da1cf55-1913-4403-a15c-f6d2c6f74430
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "7da1cf55-1913-4403-a15c-f6d2c6f74430"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Detail for a single brand tracked by AI Visibility: own brand or a specific competitor, resolved by name against the SAME ai_visibility_brands-backed set index() draws its competitors list from. The 404 below says so explicitly rather than leaving the caller to guess why a name wasn't found.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/brand"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 58bd3bf8-f29f-4ad3-8e11-fd98fadcc9cb
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "58bd3bf8-f29f-4ad3-8e11-fd98fadcc9cb"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/ai-visibility/platforms
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/platforms"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 926b8bec-4859-4ab6-86cf-73df513401e9
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "926b8bec-4859-4ab6-86cf-73df513401e9"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/ai-visibility/sentiment
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/sentiment"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 67e50fff-e461-44b8-9668-556678c03ebf
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "67e50fff-e461-44b8-9668-556678c03ebf"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/ai-visibility/citations
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/citations"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 865aa301-3553-4b57-a479-282b6c97e039
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "865aa301-3553-4b57-a479-282b6c97e039"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/ai-visibility/status
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/ai-visibility/status"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: cd967501-0453-4af9-b139-734fc11d49df
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "cd967501-0453-4af9-b139-734fc11d49df"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/agent-reports
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/agent-reports"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 8fd5e405-1d4d-4d4f-8684-b488fc96ef54
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "8fd5e405-1d4d-4d4f-8684-b488fc96ef54"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
GET api/v1/agent-reports/{run_id}
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/v1/agent-reports/17"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
x-request-id: 14cea592-93af-4fa1-9aa2-b895aa01179c
vary: Origin
{
"error": {
"code": "unauthorized",
"message": "Unauthorized.",
"docs_url": "https://docs.subsig.com/errors/unauthorized",
"request_id": "14cea592-93af-4fa1-9aa2-b895aa01179c"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
External
Trusted external onboarding (API key): create a verified user and complete onboarding in one request.
Create User And Complete Claim Onboarding
Creates a user (or reuses an existing one), then either:
- completes onboarding, or
- if a matching non-competitor project already exists under the user account, skips onboarding and returns a magic link for direct sign-in flow.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/external/claim-onboarding"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "John Doe",
"email": "john@example.com",
"organisation_name": "Acme Inc",
"product_name": "Acme CRM",
"product_website": "https:\/\/acme.com",
"product_logo": "https:\/\/cdn.example.com\/logo.png"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"message": "Onboarding completed successfully.",
"token": "1|abc123...",
"magic_link": "https://subsig-frontend.vercel.app/login?qid=1%7Cabc123...&is_claim_profile=1&org_id=550e8400-e29b-41d4-a716-446655440000",
"data": {
"organisation_uuid": "550e8400-e29b-41d4-a716-446655440000",
"product_uuid": "660e8400-e29b-41d4-a716-446655440001"
}
}
Example response (401):
{
"message": "Unauthorized."
}
Example response (422):
{
"message": "Provided organisation does not match the existing claimed profile context.",
"errors": {
"organisation_name": [
"Organisation does not match existing profile project."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Google Analytics Integration
Org-scoped via the X-Organisation-Id header (see ResolveOrganisation middleware), same
pattern as the Slack integration -- no organisation_uuid path segment, $request->organisation
is already resolved and access-checked by the time these methods run.
Connect Google Analytics Account
Exchanges an authorization code (obtained by the frontend's own OAuth flow) for tokens and creates or updates the connection. Supports multiple Google accounts per organisation -- a second call with a different account creates a second connection, not a collision.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/integrations/google-analytics/connect"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"code": "4\/0Ab_...",
"redirect_uri": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201):
{
"data": {
"id": "...",
"account_email": "user@example.com",
"status": "connected",
"connected_at": "...",
"selected_property": null
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List Google Analytics Connections
No properties included -- lazy-loaded separately per connection when its row is expanded.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/integrations/google-analytics/connections"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List GA4 Properties For A Connection
Fetched live from Google on first row-expand, briefly cached after (see GoogleAnalyticsService::listProperties()). Not eagerly included in the connections list.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/integrations/google-analytics/connections/architecto/properties"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"id": "properties/123",
"name": "Subsig - GA4",
"domain": "subscribed.fyi",
"sessions_per_month": 12400,
"matches_site": true
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Select GA4 Property For A Connection
Validated against the connection's own real property list -- a customer can only select a property their own connected account actually has. Historical rows already collected under a previously selected property are never reassigned by changing this.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/integrations/google-analytics/connections/architecto/property"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"property_id": "architecto"
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": "...",
"account_email": "...",
"status": "connected",
"connected_at": "...",
"selected_property": {
"id": "properties/123",
"name": "Subsig - GA4"
}
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Disconnect Google Analytics Account
Best-effort revoke on Google's side, then removes the connection regardless of whether the revoke call succeeds.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/integrations/google-analytics/connections/architecto"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204):
Empty response
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Home
Feeds the standalone Recommendations page, AI-Visibility-only. All the actual signal-gathering and recommendation logic lives in AiVisibilityRecommendationService, shared by nothing else, so this stays a thin request/response adapter.
GET api/organisations/{organisation_uuid}/agents/recommendations
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
The Recommendations page's top stat-card row (High priority / Open recommendations / Completed this month / AI readiness score). Pools Off-Page's Engage + Editorial cards and On-Page's cards (Technical isn't included: its checks are a pass/fail audit, not Done/Decline task-state cards -- see recommendationsForTab()'s 'technical' branch). Always a full, uncapped pull (limit: PHP_INT_MAX) since every card needs counting, not just the first page a tab would normally show.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/summary"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Off-Page/On-Page/Technical recommendation tabs -- see docs/zerorank-style-recommendations-plan.md. Separate from recommendations() above: that endpoint serves the existing flat trend-insight list (visibility drop, content-gap, etc), this one serves the new tabbed, task-tracked cards. 'off_page' (Phase 2) and 'on_page' (Phase 3) are built; 'technical' is reserved by the route's tab constraint but returns 501 until Phase 4 lands, rather than a misleading empty list.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "The route api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical could not be found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
ZeroRank's per-domain "Sources" table, shown when a card in the Off-Page sidebar is selected. Same cache/501 shape as recommendationsForTab() above.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/sources"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "The route api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/sources could not be found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
One domain's actionable cards (thread-specific, plus a subreddit-specific one for reddit.com) -- see AiVisibilityOffPageRecommendationService::cardsForDomain(). Same cache/501 shape as recommendationSources() above.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/action-cards"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "The route api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/action-cards could not be found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
ZeroRank's "Top Subreddits" panel -- only ever non-empty for a reddit.com domain. Same cache/501 shape as recommendationSources() above.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/subreddits"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "The route api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/subreddits could not be found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
"Who AI recommends from X" stacked bar -- own-brand vs named competitors vs "Others", for one Off-Page domain. Off-Page only (On-Page/Technical have no equivalent chart in the design), same cache/501 shape as recommendationSubreddits() above.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/who-recommends"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "The route api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/who-recommends could not be found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
On-Page's "Phrases, themes and domains" panel -- bundles all three into one response since the frontend always needs them together for the selected content type. Off-Page/Technical have no equivalent, same 501 shape as the other tab-scoped endpoints.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/content-insights"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (404):
Show headers
cache-control: no-cache, private
content-type: application/json
vary: Origin
{
"message": "The route api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/content-insights could not be found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Technical tab's "Re-check" button -- forces a fresh site audit, bypassing the 24h cache.
Write-shaped (an on-demand, potentially slow re-crawl of the org's own site via PageSpeed Insights + several Http::get() calls), so requires manage access like updateRecommendation TaskStatus() below, not just view access.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/recheck"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Marks one Off-Page/On-Page/Technical recommendation card Done/Declined/Todo -- see AiVisibilityRecommendationTaskStateService's docblock for why "todo" deletes rather than stores a row. Write action (changes state other users with access to this org would also see), so this requires manage access, not just view access.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/agents/recommendations/off_page|on_page|technical/status"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"item_key": "b",
"status": "architecto"
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Invites
APIs for managing invites
Validate Invite
Validate an invite token and return the associated email if the invite is valid. Also returns whether a user account already exists for the invite email, so the frontend can route to login vs signup. This endpoint is public and does not require authentication.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/invites/validate/abc123def456..."
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Accept Invite (Existing User)
requires authentication
Accept an invitation as an already-registered, authenticated user. The authenticated user's email must match the invite email. The user will be added to the organisation or project specified in the invite.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/invites/accept-invite"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"token": "eEtgjrcdtubjCu4817MfGiimvC2DQLBgaI7LpY1g5kdDMK5wJlQank7ZJ6PWurmb"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"message": "Invite accepted successfully.",
"data": {
"organisation": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Inc"
}
}
}
Example response (403, Email mismatch):
{
"message": "This invite was sent to a different email address.",
"errors": {
"email": [
"The invite email does not match your account email."
]
}
}
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, Already a member):
{
"message": "You are already a member of this organisation.",
"errors": {
"organisation": [
"You are already a member of this organisation."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List Invites
requires authentication
Get invites based on context:
- Organisation level (no project_id): Returns all organisation invites + all project invites for the organisation
- Project level (with project_id): Returns only invites for the specified project
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/invites"
);
const params = {
"organisation_id": "550e8400-e29b-41d4-a716-446655440000",
"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 (403, No access to organisation):
{
"message": "You do not have access to this organisation."
}
Example response (404, Project not found):
{
"message": "Project not found."
}
Example response (404, Organisation not found):
{
"message": "Organisation not found."
}
Example response (422, No organisation context):
{
"message": "No organisation context found.",
"errors": {
"organisation": [
"Please select an organisation or set current organisation."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create Invite
requires authentication
Create a new invite for a user to join an organisation or project. For organisation invites, the organisation is determined from the provided organisation UUID.
Example request:
const url = new URL(
"https://backend-api.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",
"organisation_id": "550e8400-e29b-41d4-a716-446655440000",
"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 (403, Inviter not whitelisted):
{
"message": "You are not permitted to send invites at this time."
}
Example response (422, No organisation context):
{
"message": "No organisation context found.",
"errors": {
"organisation": [
"Please select an organisation or set current organisation."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Invite
requires authentication
Delete an invite. Only the inviter or organisation owners can delete invites.
Example request:
const url = new URL(
"https://backend-api.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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Links
APIs for managing project links
List Project Links
requires authentication
Get all links associated with a specific project.
Example request:
const url = new URL(
"https://backend-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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",
"identity_source": "website",
"product_website": "https:\/\/acme.com",
"product_logo": "https:\/\/cdn.brandfetch.io\/acme.com\/fallback\/lettermark\/icon?c=BRANDFETCH_CLIENT_ID",
"social_profiles": [
{
"platform": "instagram",
"url": "https:\/\/instagram.com\/acme"
}
],
"threshold_consumption_date": "last_14_days. Allowed: last_14_days, last_3_months, last_6_months, last_12_months, last_2_years, last_3_years, all_time"
};
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",
"threshold_consumption_date": "2026-05-11"
},
"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"
}
}
}
Example response (422, Validation error):
{
"message": "The organisation name field is required.",
"errors": {
"organisation_name": [
"The organisation name field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Onboarding
requires authentication
Update the current organisation and project for the authenticated user.
Example request:
const url = new URL(
"https://backend-api.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",
"identity_source": "website",
"product_website": "https:\/\/acme.com",
"product_logo": "https:\/\/cdn.brandfetch.io\/acme.com\/fallback\/lettermark\/icon?c=BRANDFETCH_CLIENT_ID",
"social_profiles": [
{
"platform": "instagram",
"url": "https:\/\/instagram.com\/acme"
}
],
"threshold_consumption_date": "last_6_months"
};
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",
"threshold_consumption_date": "2026-02-25"
},
"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"
}
}
}
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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Search Google Business/Maps listings
requires authentication
Returns candidate Places for a picker UI, given a free-text query (e.g. a business name).
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/onboarding/google-places/search"
);
const params = {
"query": "Aura Bangkok Clinic Asok",
"region": "TH",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"query": "b",
"region": "ng"
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": [
{
"place_id": "ChIJ...",
"name": "Aura Bangkok Clinic Asok",
"formatted_address": "Exchange Tower, Bangkok, Thailand",
"types": [
"medical_clinic"
]
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Resolve a pasted Google Maps/Place link
requires authentication
For the "paste a link" flow (same input as Instagram/LinkedIn/etc.) -- detects a Google Maps/Place URL, resolves it to a real business via Text Search + Place Details, and returns the same shape as show() for the frontend to autofill Brand Name / switch to Website mode.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/onboarding/google-places/resolve-url"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "https:\/\/www.google.com\/maps\/place\/Aura+Bangkok+Clinic\/@13.7,100.5,17z"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"place_id": "ChIJ...",
"name": "Aura Bangkok Clinic Asok",
"primary_type": "medical_clinic",
"types": [
"medical_clinic"
],
"formatted_address": "Exchange Tower, Bangkok, Thailand",
"website_uri": "https://aurabangkokclinic.com",
"photo_names": []
}
}
Example response (404):
{
"message": "Could not resolve a business from that link."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Address autocomplete for the "Add location" modal
requires authentication
Search-as-you-type predictions for a location's physical address, scoped to one country. Distinct from search() above (which finds a whole business listing) -- this is purely an address/place-name typeahead for onboarding's multi-location "Add location" flow.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/onboarding/google-places/autocomplete"
);
const params = {
"input": "350 5th Ave",
"country": "US",
"session_token": "3fa4159e-...",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"input": "b",
"country": "ng",
"session_token": "z"
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": [
{
"place_id": "ChIJ...",
"primary_text": "350 5th Ave",
"secondary_text": "New York, NY, USA"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Address details for a picked autocomplete suggestion
requires authentication
Resolves one chosen autocomplete prediction to its full address/coordinates, for the onboarding "Add location" modal. Must be called with the SAME session_token as the autocomplete() call that produced $placeId.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/onboarding/google-places/details"
);
const params = {
"place_id": "ChIJ...",
"session_token": "3fa4159e-...",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"place_id": "b",
"session_token": "n"
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"place_id": "ChIJ...",
"formatted_address": "350 5th Ave, New York, NY 10118, USA",
"city": "New York",
"region": "New York",
"country_code": "US",
"lat": 40.7484,
"lng": -73.9857
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Google Business/Maps listing details
requires authentication
Resolves one chosen Place's details for prefilling the onboarding form.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/onboarding/google-places/ChIJ..."
);
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": {
"place_id": "ChIJ...",
"name": "Aura Bangkok Clinic Asok",
"primary_type": "medical_clinic",
"types": [
"medical_clinic"
],
"formatted_address": "Exchange Tower, Bangkok, Thailand",
"website_uri": "https://aurabangkokclinic.com",
"photo_names": []
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Organisations
APIs for managing organisations
Supported Regions
Curated list of region/country codes usable for an organisation's region (and, by
fallback, a prompt's country -- see AiVisibilityPromptController::store()). Backs the
frontend's region/country picker so it never offers a value the backend would then reject.
See config('ai_visibility.supported_regions')'s docblock for why this list isn't simply
"every ISO 3166-1 alpha-2 code" or restricted to any one provider's own narrower support.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/supported-regions"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"data": [
{
"value": "US",
"label": "United States"
},
{
"value": "GB",
"label": "United Kingdom"
}
],
"provider_region_exceptions": {
"anthropic": [
"IE",
"LU"
],
"deepseek": [
"IE",
"LU"
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List Organisations
requires authentication
Get all organisations the authenticated user belongs to.
Example request:
const url = new URL(
"https://backend-api.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"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create Organisation
requires authentication
Create a new organisation. The authenticated user becomes the admin.
Example request:
const url = new URL(
"https://backend-api.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",
"region": "architecto",
"language": "ngzmiy"
};
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"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Organisation
requires authentication
Get details of a specific organisation.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4"
);
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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Organisation
requires authentication
Update an organisation's details. Requires admin role.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"name": "Acme Corp",
"identity_source": "architecto",
"website": "https:\/\/acme.com",
"social_profiles": [
{
"platform": "instagram",
"url": "https:\/\/instagram.com\/acme"
}
]
};
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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Organisation
requires authentication
Delete an organisation and its projects (cascaded). Requires organisation owner role. Cannot delete the authenticated user's current (primary) workspace; switch first.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4"
);
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": "Organisation deleted successfully."
}
Example response (403, No access):
{
"message": "You do not have access to this organisation."
}
Example response (403, Not owner):
{
"message": "You must be an organisation admin to perform this action."
}
Example response (422, Current workspace):
{
"message": "You cannot delete your current workspace. Switch to another organisation first."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Switch Current Organisation
requires authentication
Set the user's current organisation for subsequent requests.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Platforms
APIs for managing platforms
List All Platforms
requires authentication
Get all platforms.
Example request:
const url = new URL(
"https://backend-api.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"
}
]
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create Platform
requires authentication
Create a new platform.
Example request:
const url = new URL(
"https://backend-api.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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Platform
requires authentication
Update a platform's details.
Example request:
const url = new URL(
"https://backend-api.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."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Platform
requires authentication
Delete a platform.
Example request:
const url = new URL(
"https://backend-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/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",
"created_at": "2025-12-10T10:00:00.000000Z",
"updated_at": "2025-12-10T10:00:00.000000Z"
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create Project
requires authentication
Create a new project in an organisation. Requires organisation admin role.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/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"
};
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",
"is_competitor": false,
"created_at": "2025-12-10T10:00:00.000000Z",
"updated_at": "2025-12-10T10:00:00.000000Z"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Project
requires authentication
Get details of a specific project.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/projects/b2b0131c-f2cb-42fa-ad1e-00605ce7c898"
);
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",
"created_at": "2025-12-10T10:00:00.000000Z",
"updated_at": "2025-12-10T10:00:00.000000Z"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Project
requires authentication
Update a project's details. Requires organisation admin role.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/projects/b2b0131c-f2cb-42fa-ad1e-00605ce7c898"
);
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"
};
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",
"is_competitor": false,
"created_at": "2025-12-10T10:00:00.000000Z",
"updated_at": "2025-12-10T10:00:00.000000Z"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Project
requires authentication
Delete a project. Requires organisation admin role.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/organisations/2958b10e-6a80-4903-868f-79b048b5e0c4/projects/b2b0131c-f2cb-42fa-ad1e-00605ce7c898"
);
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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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://app.subsig.com/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://app.subsig.com/dashboard/notification/overview?slack_error=architecto'" />
<title>Redirecting to https://app.subsig.com/dashboard/notification/overview?slack_error=architecto</title>
</head>
<body>
Redirecting to <a href="https://app.subsig.com/dashboard/notification/overview?slack_error=architecto">https://app.subsig.com/dashboard/notification/overview?slack_error=architecto</a>.
</body>
</html>
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
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-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Slack Connection Status
requires authentication
Returns the current Slack connection status for the organisation.
Example request:
const url = new URL(
"https://backend-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Disconnect Slack
requires authentication
Removes the Slack workspace connection for the organisation.
Example request:
const url = new URL(
"https://backend-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List Slack Channels
requires authentication
Fetches the list of channels from the connected Slack workspace.
Example request:
const url = new URL(
"https://backend-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Send a message to Slack
requires authentication
Accepts a payload in either Reviews format (data = array of reviews) or Mentions format (data = object with posts and/or comments) and posts a Block Kit message to the given Slack channel via the notification dispatcher.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/integrations/slack/send"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"channel_id": "b",
"status": "architecto",
"process_id": "architecto",
"created_at": "architecto",
"data": []
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, Success):
{
"ok": true,
"message": "Message sent."
}
Example response (400, Slack API error):
{
"ok": false,
"error": "...",
"message": "..."
}
Example response (401, Not connected):
{
"ok": false,
"error": "not_connected",
"message": "Not connected to Slack."
}
Example response (422, Invalid payload):
{
"message": "Invalid payload: data must be reviews array or mentions object."
}
Example response (500, Server error):
{
"message": "Failed to send message."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Subscriptions
APIs for managing subscriptions
List Subscription Plans
requires authentication
Get all available subscription plans.
Example request:
const url = new URL(
"https://backend-api.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_internal_free",
"name": "Free",
"description": "Post-trial free tier",
"amount": 0,
"currency": "usd",
"interval": "month",
"plan_threshold": {}
}
]
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Threshold Usage
requires authentication
Returns plan soft limits in threshold and limits.*_soft.
Usage is all-time across organisations owned by the billing user.
Per-platform breakdowns are uncapped; they may sum above the top-level totals.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/subscriptions/threshold-usage"
);
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": {
"threshold": {
"competitor_brands_limit": 3,
"history_months": 6,
"refresh_interval": "daily",
"users_limit": null,
"workspaces_limit": null,
"data_export_type": null,
"ai_visibility_prompts_limit": 20
},
"limits": {
"ai_visibility_prompts_soft": 20
},
"usage": {
"mentions": 1000,
"mentions_per_platform": {
"reddit": 200
},
"competitor_brands": 0,
"ai_visibility_prompts": 7,
"workspaces": 1,
"users": 1
},
"usage_period": {
"scope": "all_time",
"start": "2025-01-01",
"end": "2026-04-02"
},
"trial_days_left": 3
}
}
Example response (403, No organisation access):
{
"message": "You do not have access to this organisation."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
POST api/subscriptions/checkout
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/subscriptions/checkout"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"plan_id": 16,
"success_url": "http:\/\/bailey.com\/",
"cancel_url": "http:\/\/rempel.com\/sunt-nihil-accusantium-harum-mollitia",
"addons": {
"ai_visibility_prompts": {
"price": 23,
"quantity": 64
}
}
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Subscription Add-ons
requires authentication
Update add-ons on the organisation's current active subscription.
Supports optional proration_date to control Stripe proration timing.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/subscriptions/addons"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"addons": {
"ai_visibility_prompts": {
"price": 14000,
"quantity": 1000
}
},
"proration_date": 1715000000
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"id": 1,
"stripe_subscription_id": "sub_1234567890",
"status": "active",
"addons": [
{
"id": 10,
"product_id": "prod_ai_visibility_prompts_123",
"stripe_price_id": "price_inline_ai_visibility_prompts_1",
"price": 2000,
"ai_visibility_prompts_quantity": 1,
"status": "active"
}
]
}
}
Example response (403, Not owner or no organisation access):
{
"message": "You must be an organisation owner to manage subscriptions."
}
Example response (404, No active subscription):
{
"message": "No active subscription found for this organisation."
}
Example response (422, Validation error):
{
"message": "The given data was invalid."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Preview Add-on Proration
requires authentication
Preview Stripe prorated invoice impact for add-on changes without applying the subscription update.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/subscriptions/addons/preview-proration"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"addons": {
"ai_visibility_prompts": {
"price": 14000,
"quantity": 1000
}
},
"proration_date": 1715000000
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"data": {
"amount_due": 2400,
"subtotal": 2400,
"total": 2400,
"currency": "usd",
"proration_lines": [
{
"id": "il_proration_123",
"amount": 2400,
"currency": "usd",
"description": "Proration adjustment"
}
]
}
}
Example response (403, Not owner or no organisation access):
{
"message": "You must be an organisation owner to manage subscriptions."
}
Example response (404, No active subscription):
{
"message": "No active subscription found for this organisation."
}
Example response (422, Validation error):
{
"message": "The given data was invalid."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
AI Platforms Pricing
requires authentication
Cents-per-prompt rate for each AI platform add-on, keyed in the frontend's own provider vocabulary (GetMoreAiPlatformsModal.tsx multiplies this by the org's current AI Visibility Prompts quantity to render its preview) -- this is now also the exact table repriceAiPlatformsAddons() uses server-side to compute the real charge in updateAddons(), so the preview and the actual charge can never drift out of sync.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/subscriptions/ai-platforms/pricing"
);
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": {
"openai": 0,
"perplexity": 0,
"google_ai_overviews": 0,
"bing_copilot": 0,
"google_ai_mode": 43,
"gemini": 161,
"claude": 142,
"deepseek": 12,
"grok": 120
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Current Subscription
requires authentication
Get the current subscription for the organisation.
Example request:
const url = new URL(
"https://backend-api.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."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Refresh subscription from Stripe
requires authentication
Fetch the organisation's current subscription from Stripe and sync to the database. Use after the user returns from the billing portal so subscription and threshold data are up to date.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/subscriptions/refresh"
);
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):
{
"data": {
"id": 1,
"stripe_subscription_id": "sub_xxx",
"status": "active",
"current_period_start": "2025-12-01T00:00:00.000000Z",
"current_period_end": "2026-01-01T00:00:00.000000Z",
"canceled_at": null,
"ends_at": null,
"plan": { "id": 1, "stripe_price_id": "price_xxx", "name": "Pro - Monthly", ... }
}
}
Example response (403, Not owner):
{
"message": "You must be an organisation owner to manage subscriptions."
}
Example response (404, No subscription):
{
"message": "No active subscription found for this organisation."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Billing Portal URL
requires authentication
Create a Stripe Billing Portal session for the organisation's customer. The portal allows customers to manage their subscription, update payment methods, view invoices, and cancel their subscription.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/subscriptions/billing-portal"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"return_url": "https:\/\/app.example.com\/settings\/billing"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"portal_url": "https://billing.stripe.com/p/session/..."
}
Example response (403, Not owner):
{
"message": "You must be an organisation owner to manage subscriptions."
}
Example response (404, No subscription):
{
"message": "No active subscription found for this organisation."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Utilities
Validate URL Reachability
requires authentication
Validates URL format and checks if the URL is reachable.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/validate-url"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "https:\/\/example.com"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"valid": true,
"status_code": 200,
"message": "URL is valid and reachable"
}
Example response (422):
{
"valid": false,
"message": "Invalid URL format"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Webhook Tools
Utility endpoints for webhook signature testing.
Generate webhook signature
requires authentication
Generate X-Scraper-Signature using HMAC SHA256 from the exact raw_body string you send.
Use this for Swagger testing before calling scrape webhook endpoints.
Example request:
const url = new URL(
"https://backend-api.subsig.com/api/tools/webhook-signatures"
);
const headers = {
"Authorization": "Bearer 1|abc123...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"target": "social",
"raw_body": "{\"status\":\"SUCCESS\",\"process_id\":76445,\"created_at\":\"2026-01-19 14:23:11\",\"link_url\":\"https:\/\/www.trustpilot.com\/review\/example.com\",\"data\":[]}"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"signature": "8e1b1b0d7c6c1b3b8c9a..."
}
Example response (422):
{
"message": "The given data was invalid.",
"errors": {
"target": [
"The selected target is invalid."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.