Skip to main content

Management API

CRUD operations for AIRS configuration via OAuth2 client credentials. Covers security profiles, custom topics, API keys, customer apps, DLP profiles, deployment profiles, scan logs, and OAuth token management.

How it works

If the Scan API is the data plane that inspects traffic, the Management API is the control plane that decides what gets inspected and how. It's the programmatic equivalent of the AIRS configuration screens in Strata Cloud Manager — everything you'd otherwise click through, exposed as typed CRUD calls.

The thing you'll touch most is the security profile: a named ruleset that turns detectors on or off and maps each hit to allow or block. The Scan API references these profiles by name; this API is where you create, tune, and version them. Around profiles sit the supporting objects:

ResourceWhat it's for
client.profilesSecurity profiles — the rulesets scans run against. The core resource.
client.topicsCustom detection topics (your own patterns) referenced inside profiles.
client.apiKeysAIRS scan API keys for your tenant — create, rotate, revoke.
client.customerAppsCustomer application registrations.
client.dashboardPer-app token consumption + violation breakdown (SCM detail panel).
client.dlpProfilesData-loss-prevention data profiles (list).
client.deploymentProfilesDeployment profiles for the tenant (list).
client.scanLogsQuery historical scan activity by time range.
client.oauthMint / invalidate OAuth tokens for client-credential flows.

Two ideas to keep in mind:

  • One client, many sub-clients. You construct a single ManagementClient; each resource hangs off it as a property (client.profiles, client.topics, …). Auth is shared and managed for you.
  • Everything is tenant-scoped. All operations run against the Tenant Service Group (TSG) you authenticate with — there's no cross-tenant access.
Typical workflow

Create a profile (and any custom topics it needs) here → reference that profile by name from a scan → later, query scanLogs to see what it caught and refine the profile.

Authentication

The Management API uses OAuth2 client_credentials flow, separate from the scan API's API key auth. Three values are required:

Env VarRequiredDescription
PANW_MGMT_CLIENT_IDYesOAuth2 client ID from SCM
PANW_MGMT_CLIENT_SECRETYesOAuth2 client secret
PANW_MGMT_TSG_IDYesTenant Service Group ID
PANW_MGMT_ENDPOINTNoAPI base URL (default: https://api.sase.paloaltonetworks.com/aisec)
PANW_MGMT_TOKEN_ENDPOINTNoToken URL (default: https://auth.apps.paloaltonetworks.com/oauth2/access_token)

Setup

# Copy the example env file and fill in your credentials
cp .env.example .env

Or export directly:

export PANW_MGMT_CLIENT_ID=your-client-id
export PANW_MGMT_CLIENT_SECRET=your-client-secret
export PANW_MGMT_TSG_ID=1234567890

Regional Endpoints

Override PANW_MGMT_ENDPOINT for non-US deployments:

# EU
export PANW_MGMT_ENDPOINT=https://api.eu.sase.paloaltonetworks.com/aisec

# UK
export PANW_MGMT_ENDPOINT=https://api.uk.sase.paloaltonetworks.com/aisec

# FedRAMP
export PANW_MGMT_ENDPOINT=https://api.gov.sase.paloaltonetworks.com/aisec

Client Initialization

import { ManagementClient } from '@cdot65/prisma-airs-sdk';

// From env vars (recommended)
const client = new ManagementClient();

// Explicit
const client = new ManagementClient({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
tsgId: '1234567890',
});

// EU endpoint
const client = new ManagementClient({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
tsgId: '1234567890',
apiEndpoint: 'https://api.eu.sase.paloaltonetworks.com/aisec',
});

Token fetch, caching, and refresh are handled automatically. If a request gets a 401 or 403, the client refreshes the token and retries once.

Token Lifecycle

The SDK provides fine-grained control over OAuth token state via OAuthClient:

import { OAuthClient } from '@cdot65/prisma-airs-sdk';

const oauth = new OAuthClient({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
tsgId: '1234567890',
tokenBufferMs: 60_000, // refresh 60s before expiry (default: 30s)
onTokenRefresh: (info) => {
console.log(`Token refreshed, expires in ${info.expiresInMs}ms`);
},
});

// Check token state without triggering a refresh
const info = oauth.getTokenInfo();
// { hasToken, isValid, isExpired, isExpiringSoon, expiresInMs, expiresAt }

// Individual checks
oauth.isTokenExpired(); // true if past expiry time
oauth.isTokenExpiringSoon(); // true if within buffer window
oauth.isTokenExpiringSoon(120_000); // custom buffer override (2 min)

The ManagementClient handles this internally — you only need OAuthClient directly for advanced monitoring or custom auth workflows.

Security Profiles

Full CRUD on AI security profile configurations.

Create

const profile = await client.profiles.create({
profile_name: 'my-profile',
active: true,
policy: {
'ai-security-profiles': [
{
'model-type': 'default',
'model-configuration': {
'app-protection': {
'default-url-category': { member: null },
'url-detected-action': '',
},
'data-protection': {
'data-leak-detection': { action: '', member: null },
'database-security': null,
},
latency: {
'inline-timeout-action': 'block',
'max-inline-latency': 5,
},
'mask-data-in-storage': false,
'model-protection': [],
'agent-protection': [],
},
},
],
'dlp-data-profiles': [],
},
});

console.log(profile.profile_id);

Get

// Get by UUID
const profile = await client.profiles.get('profile-uuid');

// Get by name (returns highest revision if multiple exist)
const profile = await client.profiles.getByName('my-profile');

Both methods throw AISecSDKException if no matching profile is found.

List

// All profiles for the TSG
const { ai_profiles } = await client.profiles.list();

// Paginated
const page = await client.profiles.list({ offset: 0, limit: 10 });
console.log(page.next_offset); // undefined if no more pages

Update

const updated = await client.profiles.update(profile.profile_id, {
profile_name: 'my-profile-v2',
active: true,
policy: {
'ai-security-profiles': [
{
'model-type': 'default',
'model-configuration': {
'app-protection': {
'default-url-category': { member: null },
'url-detected-action': '',
},
'data-protection': {
'data-leak-detection': { action: '', member: null },
'database-security': null,
},
latency: {
'inline-timeout-action': 'allow',
'max-inline-latency': 10,
},
'mask-data-in-storage': false,
'model-protection': [],
'agent-protection': [],
},
},
],
'dlp-data-profiles': [],
},
});

Delete

const result = await client.profiles.delete(profile.profile_id);

If the profile is in use by a policy, the API returns a 409 conflict with the referencing policies.

Force Delete

// Force delete removes the profile even if referenced by a policy
// updatedBy is required for profiles
const result = await client.profiles.forceDelete(profile.profile_id, 'user@example.com');

Custom Topics

CRUD for custom detection topics used in security profiles.

Create

const topic = await client.topics.create({
topic_name: 'credit-card-numbers',
active: true,
description: 'Detects credit card numbers',
examples: ['4111-1111-1111-1111', '5500 0000 0000 0004', 'My card number is 4242424242424242'],
});

List

const { custom_topics } = await client.topics.list();
const page = await client.topics.list({ offset: 0, limit: 10 });

Update

const updated = await client.topics.update(topic.topic_id, {
topic_name: 'credit-card-numbers',
description: 'Updated description',
examples: ['4111-1111-1111-1111', 'CVV: 123'],
});

Delete

// Standard delete (fails with 409 if referenced by a profile)
const result = await client.topics.delete(topic.topic_id);

// Force delete (removes even if referenced)
// updatedBy is optional for topics
const result = await client.topics.forceDelete(topic.topic_id);
// or with updatedBy
const resultWithUser = await client.topics.forceDelete(topic.topic_id, 'user@example.com');

API Keys

Manage AIRS API keys for your TSG.

Create

const apiKey = await client.apiKeys.create({
auth_code: 'my-auth-code',
cust_app: 'my-app',
revoked: false,
created_by: 'user@example.com',
api_key_name: 'production-key',
rotation_time_interval: 90,
rotation_time_unit: 'days',
});

console.log(apiKey.api_key_id);

List

const { api_keys } = await client.apiKeys.list();

// Paginated
const page = await client.apiKeys.list({ offset: 0, limit: 10 });

Delete

const result = await client.apiKeys.delete('my-key-name', 'user@example.com');

Regenerate

const newKey = await client.apiKeys.regenerate('api-key-uuid', {
rotation_time_interval: 30,
rotation_time_unit: 'days',
});

Customer Apps

Manage customer applications for your TSG.

Get

const app = await client.customerApps.get('my-app');

List

const { customer_apps } = await client.customerApps.list();

// Paginated
const page = await client.customerApps.list({ offset: 0, limit: 10 });

Update

const updated = await client.customerApps.update('customer-app-uuid', {
app_name: 'updated-app',
cloud_provider: 'aws',
environment: 'production',
});

Delete

const result = await client.customerApps.delete('my-app', 'user@example.com');

Dashboard

Per-application token consumption and per-detector violation counts — the same data SCM renders in the AI Security > Runtime > API Applications detail panel. Pair with customerApps.list() to iterate every app in the tenant for chargeback or risk reporting.

Both methods require appId and a non-empty appName. Sending an empty appname returns HTTP 400; omitting it entirely returns an all-null body — the SDK requires both to keep those failure modes off the happy path. timeInterval is 7 | 30 | 60 (default 30), timeUnit is 'days' only (default 'days'); other values return HTTP 400.

Application overview

dashboard.application() returns token stats, session stats, attached profiles, and cloud/source metadata for one app.

const overview = await client.dashboard.application({
appId: 'd8dc4033-593b-45e7-9633-e0dfc130cc82',
appName: 'chatbot',
});

const { average_daily_tokens, average_daily_tokens_scale, monthly_total_tokens, monthly_total_tokens_scale } =
overview.token_stats ?? {};
// each numeric value is paired with a scale qualifier — 'K' (thousands) or 'M' (millions) —
// both are needed to reconstruct the SCM panel's display value

Narrow the window:

const lastWeek = await client.dashboard.application({
appId: 'd8dc4033-593b-45e7-9633-e0dfc130cc82',
appName: 'chatbot',
timeInterval: 7,
timeUnit: 'days',
});

Violation breakdown

dashboard.applicationViolationBreakdown() returns one entry per detector in detection_type_violation_breakdown[], each with critical/high/medium/low/total severity counts, plus the rolled-up total_violating.

const breakdown = await client.dashboard.applicationViolationBreakdown({
appId: 'd8dc4033-593b-45e7-9633-e0dfc130cc82',
appName: 'chatbot',
});

for (const entry of breakdown.detection_type_violation_breakdown ?? []) {
if ((entry.violation_breakdown?.total ?? 0) === 0) continue;
console.log(entry.detection_type, entry.violation_breakdown);
}
// breakdown.total_violating // rolled-up count across all detectors

Detector codes observed live (10 as of 2026-05-28): agent_security, contextual_grounding, dbs (database security), dlp, malicious_code, pi (prompt injection), source_code, tc (toxic content), topic_guardrails, uf (URL filtering). Schemas use .passthrough(), so new detectors parse cleanly without an SDK bump.

Per-app chargeback pattern

Combine customerApps.list() with dashboard.application() to attribute token spend across every app in the tenant:

const { customer_apps = [] } = await client.customerApps.list({ offset: 0, limit: 100 });
const scale = (n?: number | null, s?: string | null) =>
(n ?? 0) * (s === 'M' ? 1_000_000 : s === 'K' ? 1_000 : 1);

for (const app of customer_apps) {
if (!app.customer_appId) continue;
const overview = await client.dashboard.application({
appId: app.customer_appId,
appName: app.app_name,
});
const tokens = scale(
overview.token_stats?.monthly_total_tokens,
overview.token_stats?.monthly_total_tokens_scale,
);
console.log(`${app.app_name}: ${tokens.toLocaleString()} tokens this month`);
}

See examples/mgmt-dashboard.ts for a runnable version that also surfaces firing detectors per app.

DLP Profiles

List DLP data profiles configured for the TSG.

const { dlp_profiles } = await client.dlpProfiles.list();

Deployment Profiles

List deployment profiles for the TSG.

// All deployment profiles
const { deployment_profiles } = await client.deploymentProfiles.list();

// Include unactivated profiles
const all = await client.deploymentProfiles.list({ unactivated: true });

Scan Logs

Query scan activity logs by time range.

const results = await client.scanLogs.query({
time_interval: 24,
time_unit: 'hour',
pageNumber: 1,
pageSize: 50,
filter: 'all', // 'all', 'benign', or 'threat'
});

console.log(results.total_pages);
console.log(results.scan_results);

// Continue pagination with page_token
const nextPage = await client.scanLogs.query({
time_interval: 24,
time_unit: 'hour',
pageNumber: 2,
pageSize: 50,
filter: 'all',
page_token: results.page_token,
});

OAuth Token Management

Manage OAuth tokens for client credential flows.

Get Access Token

const token = await client.oauth.getAccessToken({
body: { client_id: 'cid', customer_app: 'my-app' },
tokenTtlInterval: 24,
tokenTtlUnit: 'hours',
});

console.log(token.access_token);

Invalidate Token

await client.oauth.invalidateToken('token-value', {
client_id: 'cid',
customer_app: 'my-app',
});

Error Handling

import { AISecSDKException, ErrorType } from '@cdot65/prisma-airs-sdk';

try {
await client.profiles.list();
} catch (err) {
if (err instanceof AISecSDKException) {
switch (err.errorType) {
case ErrorType.OAUTH_ERROR:
console.error('Auth failed:', err.message);
break;
case ErrorType.CLIENT_SIDE_ERROR:
console.error('Bad request:', err.message);
break;
case ErrorType.SERVER_SIDE_ERROR:
console.error('Server error:', err.message);
break;
case ErrorType.MISSING_VARIABLE:
console.error('Missing config:', err.message);
break;
}
}
}

Get the most out of it

Reuse one client

Build a single ManagementClient and share it across your app. It owns the OAuth token cache — every sub-client (profiles, topics, …) shares the same token, so you authenticate once and reuse it everywhere. Constructing a fresh client per call throws that cache away.

Delete is referential — expect 409s

A standard delete on a profile or topic that's still referenced by a policy fails with a 409 conflict (the response lists the referencing policies). That's a safety net, not a bug. Either detach the references first, or call forceDelete to remove it anyway. Note the asymmetry:

  • profiles.forceDelete(id, updatedBy)updatedBy is required.
  • topics.forceDelete(id, updatedBy?)updatedBy is optional.
List endpoints paginate

list() returns up to 100 items by default. Pass { offset, limit } and follow next_offset (or page_token for scan logs) until it comes back undefined to walk the full set. Don't assume the first page is everything.

Look profiles up by name when you can. profiles.getByName('my-profile') saves you a list-then-filter, and returns the highest revision if several exist. Both get and getByName throw AISecSDKException when nothing matches — handle that rather than expecting null.

Rotate keys, don't recreate them. apiKeys.regenerate(id, …) issues a new secret while preserving the key's identity and rotation policy — cleaner than deleting and re-adding.

Tune profiles from real traffic. Use scanLogs.query({ time_interval, time_unit, filter: 'threat' }) to see what a profile actually caught (or missed), then adjust detectors and custom topics. Close the loop instead of guessing.

Set the right region. All control-plane calls share one OAuth identity but must target the matching regional endpoint — override PANW_MGMT_ENDPOINT (see Regional Endpoints) for EU/UK/FedRAMP tenants.

Retries are automatic. Like the scan client, management requests retry on transient 5xx errors with backoff, and refresh-and-retry once on 401/403. Tune attempts with numRetries (0–5). See the OAuth lifecycle for the token side of this.

Full reference

Every sub-client method on ManagementClient — with input and output examples — is in the Full API reference.

Running the Examples

# Copy env file and fill in credentials
cp .env.example .env

# Run examples (require credentials)
npm run example:mgmt-auth
npm run example:mgmt-profiles
npm run example:mgmt-topics

# Self-contained validation (no credentials needed — uses mock servers)
npm run example:profiles-get # get() and getByName() methods
npm run example:profiles-crud # full CRUD lifecycle (create/list/get/update/delete/force-delete)