Skip to content

Brands and client portals

Create brands and manage white-label portal access from your own billing system.

Code example

Create a brand
curl -X POST 'https://api.trakkr.ai/manage-brand' \
  -H "Authorization: Bearer $TRAKKR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"action":"create","domain":"clinic.example","name":"Clinic","location":"Saudi Arabia","language":"ar","prompts":["best dental clinic in Riyadh","emergency dentist open now Riyadh"]}'
Response · 200 OK
{
  "action": "create",
  "success": true,
  "brand_id": "00000000-0000-4000-8000-81f286d10c3c",
  "active": true,
  "created": true,
  "restored": false,
  "already_existed": false,
  "prompts_created": 2,
  "prompts_generated": false,
  "report_triggered": true,
  "creation_allowance": {
    "limited": true,
    "limit": 20,
    "used": 4,
    "remaining": 16,
    "window_days": 30,
    "next_available_at": null
  }
}

Synthetic example. Values are made up.

Invite a client
curl -X POST 'https://api.trakkr.ai/manage-client' \
  -H "Authorization: Bearer $TRAKKR_API_KEY" \
  -H 'Idempotency-Key: evt_1QxYz2LkdIwHu7ix0abc' \
  -H 'Content-Type: application/json' \
  -d '{"action":"invite","brand_id":"00000000-0000-4000-8000-81f286d10c3c","email":"[email protected]","client_name":"Clinic owner","send_email":true,"expires_days":14}'
Response · 200 OK
{
  "action": "invite",
  "success": true,
  "client": {
    "id": "00000000-0000-4000-8000-5a3c9e1f2b77",
    "email": "[email protected]",
    "brand_id": "00000000-0000-4000-8000-81f286d10c3c",
    "brand_name": "Clinic",
    "client_name": "Clinic owner",
    "status": "pending",
    "can_export": false,
    "expires_at": "2026-10-30T12:00:00Z",
    "invite_url": "https://portal.youragency.example/invite/abc123",
    "email_sent": true
  }
}

Synthetic example. Values are made up.

Revoke access
curl -X POST 'https://api.trakkr.ai/manage-client' \
  -H "Authorization: Bearer $TRAKKR_API_KEY" \
  -H 'Idempotency-Key: evt_1QxZa8LkdIwHu7ixRevoke' \
  -H 'Content-Type: application/json' \
  -d '{"action":"revoke","brand_id":"00000000-0000-4000-8000-81f286d10c3c","email":"[email protected]","reason":"Subscription ended"}'

API key required

20 req/min

What these are for

If you sell AI visibility to your own customers, these two endpoints let your billing system do the setup. When a customer subscribes you create their brand and invite them to your white-label portal. When they stop paying you revoke their access, and when they come back you restore it. Each call does exactly what the same action does in the app, with the same checks.

Before you start:

  • Use a REST API key on the Scale or Enterprise plan. The MCP connector cannot call these endpoints.
  • The key should belong to a team owner or admin. Brands you create belong to that team.
  • To invite clients, finish white-label setup in the app first: your portal domain must be verified. See The white-label portal.

Manage brands

POST/manage-brand

Authenticate with Authorization: Bearer $TRAKKR_API_KEY. Key and access guide

JSON request body (required)

actionstringRequired

Options: "create", "pause", "resume", "delete", "restore"

brand_idstring | nullOptional

The brand to change. Required for every action except create.

Format: uuid

domainstring | nullOptional

create: the brand's website, for example example.com.

Maximum length: 253

namestring | nullOptional

create: display name. Defaults to the name found on the site.

Maximum length: 120

descriptionstring | nullOptional

Maximum length: 2000

industrystring | nullOptional

Maximum length: 200

target_audiencestring | nullOptional

Maximum length: 500

locationstring | nullOptional

create: country to track from, as a name or ISO code (for example 'Saudi Arabia' or 'SA'). Omit for worldwide.

Maximum length: 64

languagestring | nullOptional

create: prompt language as a name or code (for example 'Arabic' or 'ar'). Defaults to the site's language.

Maximum length: 32

timezonestring | nullOptional

create: IANA timezone, for example Asia/Riyadh.

Maximum length: 64

promptsstring[] | nullOptional

create: the prompts to track. Omit to have Trakkr write them from the site.

Maximum items: 200

competitorsstring[] | nullOptional

create: competitor names or domains.

Maximum items: 50

allow_duplicate_domainbooleanOptional

create: add another brand on a domain you already track, for example a second market.

Default: false

Responses and errors
200Successful Response

application/json

actionstringRequired

successbooleanOptional

Default: true

brand_idstringRequired

activeboolean | nullOptional

Whether the brand is tracking now.

createdbooleanOptional

create: a new brand was made.

Default: false

restoredbooleanOptional

create/restore: a recently deleted brand came back with its history.

Default: false

already_existedbooleanOptional

create: a retry of an earlier create returned that brand.

Default: false

prompts_createdinteger | nullOptional

prompts_generatedbooleanOptional

create: Trakkr wrote the prompts from the site.

Default: false

report_triggeredboolean | nullOptional

A first report was queued.

creation_allowanceobject | nullOptional

create: new brands allowed per 30 days on this plan, used, and remaining.

messagestring | nullOptional

401Missing, malformed, or expired credentials

application/json

detailstring | object | nullOptional

errorstring | nullOptional

402The current plan does not fund this operation

application/json

detailstring | object | nullOptional

errorstring | nullOptional

403Invalid credentials, missing brand access, or insufficient role

application/json

detailstring | object | nullOptional

errorstring | nullOptional

422Validation Error

application/json

detailobject[]Optional

Fields in detail

Array of object.

locstring | integer[]Required

Fields in loc

Array of string | integer.

string

string.

integer

integer.

msgstringRequired

typestringRequired

429Rate limit exceeded

application/json

detailstring | object | nullOptional

errorstring | nullOptional

500The operation failed

application/json

detailstring | object | nullOptional

errorstring | nullOptional

503Authentication, entitlement, or a required dependency is temporarily unavailable

application/json

detailstring | object | nullOptional

errorstring | nullOptional

OpenAPI source

Set action to create, pause, resume, delete or restore. Every action except create takes a brand_id.

create adds a brand from its domain. Send your own prompts to track the questions your customer cares about, such as local searches in their city and language. Leave them out and Trakkr reads the site and writes them, along with the name, description and competitors. Set location to the country to track from and language to the prompt language. The brand starts tracking straight away and its first report is queued.

Creating is safe to retry. Sending the same create again returns the brand the first request made, with already_existed set. If you deleted a brand on the same domain in the last 7 days, create brings it back with its history and sets restored, rather than starting again.

pause stops tracking and frees the brand slot. Its data stays, and so does portal access. resume starts it again when a slot is free. delete removes the brand and its clients lose access; only the brand owner can delete. restore brings back a brand deleted in the last 7 days, with its portal clients.

Two limits apply to create. It needs a free brand slot, or it returns 409 brand_limit_reached. And each account can add up to twice its brand slots in new brands every 30 days (its slots plus five on smaller plans), counting brands later deleted. Past that it returns 429 brand_creation_limit_reached. Every create response includes creation_allowance so you can see how much is left.

Manage client access

POST/manage-client

Authenticate with Authorization: Bearer $TRAKKR_API_KEY. Key and access guide

Request parameters

Idempotency-Keystring | nullheaderOptional

Required for invite, revoke and restore: a key unique to this change, for example your Stripe event ID (12-200 characters).

JSON request body (required)

actionstringRequired

Options: "list", "invite", "revoke", "restore", "resend", "update"

brand_idstring | nullOptional

The brand the client sees. Required for invite; with email, identifies a client for the other actions; filters list.

Format: uuid

client_idstring | nullOptional

The client record, from invite or list.

Format: uuid

emailstring | nullOptional

The client's email address.

Maximum length: 254

client_namestring | nullOptional

Maximum length: 120

personal_messagestring | nullOptional

invite: a note shown in the invite email.

Maximum length: 1000

send_emailbooleanOptional

invite/restore: send Trakkr's branded invite email. Set false to send the returned invite_url yourself.

Default: true

expires_daysintegerOptional

invite/restore: days before the invite link expires.

Default: 14 · Minimum: 7 · Maximum: 30

can_exportboolean | nullOptional

invite/restore/update: let the client export data from the portal. Invite defaults to false; restore keeps the saved setting.

receives_weekly_reportsboolean | nullOptional

invite/restore/update: email the client a weekly report. Invite defaults to true; restore keeps the saved setting.

statusstring | nullOptional

list: only clients in this state.

Options: "pending", "active", "revoked"

reasonstring | nullOptional

revoke: kept on the audit trail.

Maximum length: 500

Responses and errors
200Successful Response

application/json

actionstringRequired

successbooleanOptional

Default: true

clientobject | nullOptional

Fields in client
PublicPortalClient

idstringRequired

emailstringRequired

brand_idstringRequired

brand_namestring | nullOptional

client_namestring | nullOptional

statusstringRequired

pending (invited), active (accepted) or revoked.

can_exportboolean | nullOptional

receives_weekly_reportsboolean | nullOptional

created_atstring | nullOptional

accepted_atstring | nullOptional

expires_atstring | nullOptional

When a pending invite link stops working.

last_active_atstring | nullOptional

invite_urlstring | nullOptional

The invite link, returned when an invite is created or resent.

email_sentboolean | nullOptional

null

null.

clientsobject[] | nullOptional

totalinteger | nullOptional

already_revokedboolean | nullOptional

messagestring | nullOptional

401Missing, malformed, or expired credentials

application/json

detailstring | object | nullOptional

errorstring | nullOptional

402The current plan does not fund this operation

application/json

detailstring | object | nullOptional

errorstring | nullOptional

403Invalid credentials, missing brand access, or insufficient role

application/json

detailstring | object | nullOptional

errorstring | nullOptional

422Validation Error

application/json

detailobject[]Optional

Fields in detail

Array of object.

locstring | integer[]Required

Fields in loc

Array of string | integer.

string

string.

integer

integer.

msgstringRequired

typestringRequired

429Rate limit exceeded

application/json

detailstring | object | nullOptional

errorstring | nullOptional

500The operation failed

application/json

detailstring | object | nullOptional

errorstring | nullOptional

503Authentication, entitlement, or a required dependency is temporarily unavailable

application/json

detailstring | object | nullOptional

errorstring | nullOptional

OpenAPI source

Set action to invite, revoke, restore, resend, update or list. A client is one email address with read-only access to one brand in your portal. Identify one by client_id, or by brand_id and email, which is usually what your billing system knows.

invite sends your branded invite email. Set send_email to false to send it yourself instead: the response includes the invite_url. revoke ends access straight away. restore sends a revoked client a new invite, and access returns when they accept it. resend sends a pending invite again with a fresh expiry. update changes a client's can_export, receives_weekly_reports or client_name without touching their access. list returns your team's clients, optionally for one brand or status.

Invite, revoke and restore need an Idempotency-Key header, so a retried request never changes access twice. Use a value unique to the change, such as the ID of the Stripe event that triggered it.

A subscription from start to finish

This is how the calls map onto a typical billing system. The same pattern works with Stripe, Paddle, Chargebee or your own.

WhenCall
A customer subscribesPOST /manage-brand with action create, then POST /manage-client with action invite
The customer accepts the inviteYou receive the client_invite_accepted webhook
A payment fails or the subscription pausesPOST /manage-client with action revoke. Optionally pause the brand to free its slot
The customer pays againPOST /manage-brand with action resume, then POST /manage-client with action restore
The subscription ends for goodPOST /manage-client with action revoke, then POST /manage-brand with action delete

To hear about portal access changes, subscribe a brand's webhook to client_invite_accepted and client_access_revoked. Both carry the client ID, email and brand.

Errors

Errors come back as {"detail": {"code": "...", "message": "..."}}. Branch on code; the message is written to show a person.

StatusCodeMeaning
403rest_key_requiredCalled through the MCP connector. Use a REST API key.
403team_admin_requiredThe key belongs to someone who is not a team owner or admin.
403delete_not_permittedOnly the brand owner can delete it.
409brand_limit_reachedEvery brand slot is in use. Pause or delete a brand, or add slots.
409duplicate_domainThis domain is already tracked. The response names the existing brand.
422prompt_generation_failedTrakkr could not read the site. Send prompts with the request.
429brand_creation_limit_reachedThe 30-day new-brand allowance is used. The response says when the next one fits.
400idempotency_key_requiredInvite, revoke and restore need an Idempotency-Key header.