Brands and client portals
Create brands and manage white-label portal access from your own billing system.
Code example
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"]}'{
"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.
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}'{
"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.
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/minWhat 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
/manage-brandAuthenticate 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
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.
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
/manage-clientAuthenticate 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
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.
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.
| When | Call |
|---|---|
| A customer subscribes | POST /manage-brand with action create, then POST /manage-client with action invite |
| The customer accepts the invite | You receive the client_invite_accepted webhook |
| A payment fails or the subscription pauses | POST /manage-client with action revoke. Optionally pause the brand to free its slot |
| The customer pays again | POST /manage-brand with action resume, then POST /manage-client with action restore |
| The subscription ends for good | POST /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.
| Status | Code | Meaning |
|---|---|---|
| 403 | rest_key_required | Called through the MCP connector. Use a REST API key. |
| 403 | team_admin_required | The key belongs to someone who is not a team owner or admin. |
| 403 | delete_not_permitted | Only the brand owner can delete it. |
| 409 | brand_limit_reached | Every brand slot is in use. Pause or delete a brand, or add slots. |
| 409 | duplicate_domain | This domain is already tracked. The response names the existing brand. |
| 422 | prompt_generation_failed | Trakkr could not read the site. Send prompts with the request. |
| 429 | brand_creation_limit_reached | The 30-day new-brand allowance is used. The response says when the next one fits. |
| 400 | idempotency_key_required | Invite, revoke and restore need an Idempotency-Key header. |
