Skip to main content

Authentication

AudienceGPT supports three authentication methods to cover dashboard users, server-to-server integrations, and client-side embedded widgets. Every API request must include valid credentials via one of these methods; unauthenticated requests receive a 401 Unauthorized response.

Authentication Methods

1. Clerk Sessions (Dashboard)

Browser-based authentication for dashboard users. Clerk manages session cookies automatically when users sign in through the AudienceGPT web interface.

  • Transport: HTTP-only session cookie (set by Clerk)
  • Use case: All dashboard interactions, admin operations, credential management
  • Org scoping: The active Clerk organization determines the orgId for all queries
  • Role-based permissions: Clerk org roles map to application roles (owner, admin, member, viewer) with granular permissions
info

Clerk sessions are required for operations that manage credentials, such as creating connections or managing API keys. API keys cannot perform these operations.

2. API Keys (txadv_ prefix)

Self-managed API keys for server-to-server integrations. Each key is scoped to an organization and can be restricted to specific permission scopes.

  • Prefix: txadv_
  • Transport: Authorization: Bearer txadv_... header
  • Max keys per org: 25
  • Expiration: Optional expiration date (ISO 8601)
  • Revocation: Immediate via the /api/keys endpoint
curl -X POST https://app.audiencegpt.com/api/classify \
-H "Authorization: Bearer txadv_a8Kz3mN9pQ2wX..." \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Classify: Tesla Model 3 buyers"}]}'

3. SDK Publishable Keys (pk_live_ / pk_test_ prefix)

Client-side keys for embedding AudienceGPT classification in consumer-facing applications. These keys are safe to expose in browser JavaScript because they are scoped to specific operations.

  • Prefixes: pk_live_ (production), pk_test_ (testing)
  • Transport: Authorization: Bearer pk_live_... header
  • CORS: SDK endpoints return Access-Control-Allow-Origin: * headers
  • Routes: All /api/sdk/* endpoints accept SDK keys
const response = await fetch("https://app.audiencegpt.com/api/sdk/classify", {
method: "POST",
headers: {
"Authorization": "Bearer pk_live_x9Kz3mN9pQ2wX...",
"Content-Type": "application/json",
},
body: JSON.stringify({
messages: [{ role: "user", content: "Classify: organic skincare" }],
}),
});

Authorization Scopes

API keys and SDK keys can be restricted to specific scopes. When creating a key, you can specify which scopes to grant. If no scopes are specified, all scopes are granted by default.

ScopeDescriptionEndpoints
classifyClassify topics, analyze briefs, check duplicates/api/classify, /api/analyze-brief, /api/topics/generate-matrix
topics:readRead topics, stats, facets, history, catalog/api/topics (GET), /api/topics/stats, /api/topics/facets, /api/topics/catalog
topics:writeCreate, update, delete topics; run imports and reclassify/api/topics (POST/DELETE), /api/import, /api/topics/reclassify
exportExport topics as CSV or JSON/api/export
syncManage connections, run syncs/api/connections, /api/connections/*/sync/*
activationsManage segment activations, push to DSPs/api/activations, /api/connections/*/push/*
mappings:readRead platform ID mappings/api/sdk/mappings

API Key Management

Create an API Key

Requires a Clerk session with api_keys:manage permission.

POST /api/keys

Request body:

{
"name": "Production Integration",
"scopes": ["classify", "topics:read", "topics:write"],
"expiresAt": "2027-01-01T00:00:00Z"
}
FieldTypeRequiredDescription
namestringYesHuman-readable name (max 100 characters)
scopesstring[]NoPermission scopes (defaults to all scopes)
expiresAtstringNoISO 8601 expiration date (must be in the future)

Response (201):

{
"record": {
"id": "key_01hy...",
"orgId": "org_2abc...",
"createdBy": "user_2xyz...",
"name": "Production Integration",
"keyPrefix": "txadv_a8Kz3m",
"scopes": ["classify", "topics:read", "topics:write"],
"expiresAt": "2027-01-01T00:00:00.000Z",
"lastUsedAt": null,
"revokedAt": null,
"createdAt": "2026-02-25T10:00:00.000Z"
},
"plaintext": "txadv_a8Kz3mN9pQ2wXyZ5bC7dE..."
}
warning

The plaintext key is only returned once at creation time. Store it securely -- it cannot be retrieved again.

List API Keys

GET /api/keys

Response (200):

[
{
"id": "key_01hy...",
"orgId": "org_2abc...",
"createdBy": "user_2xyz...",
"name": "Production Integration",
"keyPrefix": "txadv_a8Kz3m",
"scopes": ["classify", "topics:read", "topics:write"],
"expiresAt": "2027-01-01T00:00:00.000Z",
"lastUsedAt": "2026-02-25T12:00:00.000Z",
"revokedAt": null,
"createdAt": "2026-02-25T10:00:00.000Z"
}
]

Revoke an API Key

DELETE /api/keys

Request body:

{
"id": "key_01hy..."
}

Response (200):

{
"revoked": true
}

SDK Key Management

Create an SDK Key

POST /api/sdk-keys

Request body:

{
"name": "Website Widget",
"mode": "live",
"scopes": ["classify", "topics:read"]
}
FieldTypeRequiredDescription
namestringYesHuman-readable name (max 100 characters)
modestringNo"live" (default) or "test"
scopesstring[]NoPermission scopes (defaults to all scopes)

Response (201):

{
"record": {
"id": "sdk_01hy...",
"orgId": "org_2abc...",
"name": "Website Widget",
"keyPrefix": "pk_live_",
"scopes": ["classify", "topics:read"],
"revokedAt": null,
"createdAt": "2026-02-25T10:00:00.000Z"
},
"plaintext": "pk_live_x9Kz3mN9pQ2wXyZ5bC..."
}

List SDK Keys

GET /api/sdk-keys

Revoke an SDK Key

DELETE /api/sdk-keys

Request body:

{
"id": "sdk_01hy..."
}

Auth Resolution Order

When a request arrives, the authentication system resolves credentials in this order:

  1. Check Authorization header for Bearer txadv_... -- if present, hash the key and look up in the api_keys table
  2. Check Authorization header for Bearer pk_... -- SDK routes resolve from the sdk_keys table
  3. Fall back to Clerk session -- check for a valid Clerk session cookie with an active organization

If none of these succeed, the request is rejected with 401 Unauthorized.

tip

The optional X-Workspace-Id header lets you scope requests to a specific workspace within your organization. Pass it alongside any authentication method.

Error Responses

401 Unauthorized

Returned when no valid credentials are provided or the key is expired/revoked.

{
"error": "Unauthorized"
}

403 Forbidden

Returned when credentials are valid but the key lacks the required scope.

{
"error": "Forbidden"
}

Scope Insufficient (API Key)

When an API key is valid but lacks a required scope, the system returns 401 (the scope check failure is opaque to prevent information leakage about key validity).

TypeScript Example

// Server-side: API key authentication
const API_KEY = process.env.AUDIENCEGPT_API_KEY; // txadv_...

async function classifyTopic(topicName: string) {
const response = await fetch("https://app.audiencegpt.com/api/classify", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
messages: [
{ role: "user", content: `Classify: ${topicName}` },
],
}),
});

if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}

return response.json();
}

Next Steps