TypeScript SDK Reference
The cPod TypeScript SDK gives you typed access to the 6 domains of the Enterprise Data Model: People, Technology, Licenses, Assets, Risk & Compliance, and Common.
Package: @cpod/sdk
NPM: npmjs.com/package/@cpod/sdk
Source: github.com/cpod-ai/cpod-sdk/tree/main/sdks/typescript
Node.js: ≥ 18.0.0
Installation
npm install @cpod/sdk
# or
pnpm add @cpod/sdk
# or
yarn add @cpod/sdkBasic Import
import { CpodClient } from '@cpod/sdk'
const client = new CpodClient({
apiKey: process.env.CPOD_API_KEY!,
})Quick Example
The following shows a representative real-world query: look up a person, find the technology assets they have access to, check their active entitlements, and list any vulnerabilities on those assets.
import { CpodClient } from '@cpod/sdk'
const client = new CpodClient({ apiKey: process.env.CPOD_API_KEY! })
async function personSecurityProfile(personEmail: string) {
// 1. Find the person by email
const { data: people } = await client.people.list({ search: personEmail })
const person = people[0]
if (!person) throw new Error(`Person not found: ${personEmail}`)
console.log(`Person: ${person.displayName} (${person.type}, ${person.status})`)
// 2. Get all technology assets they have access to
const { data: entitlements } = await client.technology.getEntitlements(person.id)
const activeEntitlements = entitlements.filter(e => e.status === 'active')
console.log(`Active entitlements: ${activeEntitlements.length}`)
// 3. For each asset, check for open vulnerabilities
const assetIds = [...new Set(activeEntitlements.map(e => e.assetId))]
const vulnChecks = await Promise.all(
assetIds.map(assetId =>
client.risk.listVulnerabilities({ affectedAssetId: assetId, status: 'open' })
)
)
const criticalVulns = vulnChecks
.flatMap(r => r.data)
.filter(v => v.severity === 'critical' || v.severity === 'high')
console.log(`Critical/High open vulnerabilities on accessible assets: ${criticalVulns.length}`)
return { person, activeEntitlements, criticalVulns }
}CpodClient
The root client. All domain services are namespaced under it.
const client = new CpodClient(config: CpodConfig)CpodConfig
interface CpodConfig {
/** Your cPod API key. Required. */
apiKey: string
/** Base URL of the cPod API. Default: 'https://api.cyberpod.app' */
baseUrl?: string
/** Request timeout in milliseconds. Default: 30000 */
timeout?: number
}Domain Service Properties
| Property | Type | Domain |
|---|---|---|
client.people | PeopleService | People |
client.groups | GroupService | People |
client.technology | TechnologyService | Technology |
client.licenses | LicenseService | Licenses |
client.assets | AssetService | Assets |
client.risk | RiskService | Risk & Compliance |
client.relationships | RelationshipService | Common |
client.dataSources | DataSourceService | Common |
People Domain
PeopleService — client.people
list(opts?, requestOpts?)
const { data: people, total } = await client.people.list({
type: 'employee', // 'employee' | 'contractor' | 'vendor' | 'partner' | 'service_account'
status: 'active', // 'active' | 'on_leave' | 'terminated' | 'suspended'
department: 'Engineering',
search: 'jane', // searches displayName, email, employeeId
page: 1,
pageSize: 50,
})get(id, requestOpts?)
const person = await client.people.get('person-uuid')
// person.email, person.displayName, person.department, person.managerId, ...create(input, requestOpts?)
const newPerson = await client.people.create({
email: 'jane.doe@acme.com',
displayName: 'Jane Doe',
firstName: 'Jane',
lastName: 'Doe',
type: 'employee',
department: 'Engineering',
title: 'Senior Engineer',
location: { site: 'HQ', city: 'San Francisco', country: 'US', remote: false },
managerId: 'manager-uuid',
})update(id, input, requestOpts?)
const updated = await client.people.update('person-uuid', {
title: 'Staff Engineer',
department: 'Platform Engineering',
})deactivate(id, requestOpts?)
// Transitions person status to 'terminated'. Does not revoke entitlements automatically.
const deactivated = await client.people.deactivate('person-uuid')GroupService — client.groups
list(requestOpts?)
const { data: groups } = await client.groups.list()get(id, requestOpts?)
const group = await client.groups.get('group-uuid')
// group.name, group.type, group.members, group.ownerId, ...create(input, requestOpts?)
const group = await client.groups.create({
name: 'Platform Team',
type: 'team',
description: 'Core platform engineering team',
ownerId: 'person-uuid',
source: 'manual',
})addMember(groupId, personId, role?, requestOpts?)
await client.groups.addMember('group-uuid', 'person-uuid', 'lead')removeMember(groupId, personId, requestOpts?)
await client.groups.removeMember('group-uuid', 'person-uuid')Technology Domain
TechnologyService — client.technology
list(requestOpts?)
const { data: assets } = await client.technology.list()
const criticalApps = assets.filter(a => a.criticality === 'critical' && a.status === 'active')get(id, requestOpts?)
const asset = await client.technology.get('asset-uuid')
// asset.name, asset.vendor, asset.criticality, asset.dataClassification, ...create(input, requestOpts?)
const asset = await client.technology.create({
name: 'salesforce-crm',
displayName: 'Salesforce CRM',
type: 'saas',
category: 'CRM',
vendor: 'Salesforce',
owner: { personId: 'owner-uuid' },
environment: 'production',
hosting: 'saas',
criticality: 'critical',
dataClassification: 'confidential',
tags: ['sales', 'crm'],
})update(id, input, requestOpts?)
const updated = await client.technology.update('asset-uuid', {
version: '2024.3',
status: 'active',
})getEntitlements(assetId, requestOpts?)
Returns all AccessEntitlement records for the given asset — use this for access reviews.
const { data: entitlements } = await client.technology.getEntitlements('asset-uuid')
// Filter to only active, non-expiring entitlements held by humans (not groups)
const activeHumanAccess = entitlements.filter(
e => e.status === 'active' && e.principalType === 'person'
)grantEntitlement(input, requestOpts?)
const entitlement = await client.technology.grantEntitlement({
assetId: 'asset-uuid',
principalId: 'person-uuid',
principalType: 'person',
entitlementType: 'read',
expiresAt: '2026-12-31T23:59:59Z',
})revokeEntitlement(id, requestOpts?)
const revoked = await client.technology.revokeEntitlement('entitlement-uuid')
// revoked.status === 'revoked'Licenses Domain
LicenseService — client.licenses
list(requestOpts?)
const { data: licenses } = await client.licenses.list()
const overAllocated = licenses.filter(l => l.complianceStatus === 'over_allocated')
const expiringSoon = licenses.filter(l => l.status === 'expiring_soon')get(id, requestOpts?)
const license = await client.licenses.get('license-uuid')
// license.totalSeats, license.usedSeats, license.availableSeats, license.complianceStatuscreate(input, requestOpts?)
const license = await client.licenses.create({
vendor: 'Atlassian',
productName: 'Jira Software',
productSku: 'JIRA-CLOUD-ENTERPRISE',
licenseType: 'subscription',
totalSeats: 500,
cost: { amount: 45000, currency: 'USD', billingCycle: 'annual' },
purchaseDate: '2026-01-01',
expiryDate: '2027-01-01',
autoRenew: true,
})getAssignments(licenseId, requestOpts?)
const { data: assignments } = await client.licenses.getAssignments('license-uuid')
const activeAssignments = assignments.filter(a => a.status === 'active')assign(licenseId, assigneeId, assigneeType, requestOpts?)
const assignment = await client.licenses.assign(
'license-uuid',
'person-uuid',
'person'
)unassign(assignmentId, requestOpts?)
await client.licenses.unassign('assignment-uuid')Assets Domain
AssetService — client.assets
listPhysical(requestOpts?) / getPhysical(id, requestOpts?)
const { data: laptops } = await client.assets.listPhysical()
const lost = laptops.filter(a => a.status === 'lost_stolen')
const laptop = await client.assets.getPhysical('asset-uuid')createPhysical(input, requestOpts?)
const laptop = await client.assets.createPhysical({
assetTag: 'HW-00423',
name: "Jane's MacBook Pro",
model: 'MacBook Pro 14-inch, M3 Pro',
manufacturer: 'Apple',
serialNumber: 'C02XG1JFJGH5',
type: 'laptop',
status: 'in_use',
assignedTo: { personId: 'person-uuid' },
purchaseDate: '2025-09-01',
warrantyExpiry: '2028-09-01',
os: { name: 'macOS', version: '15.3' },
managedBy: 'jamf',
})listCloud(requestOpts?) / getCloud(id, requestOpts?)
const { data: resources } = await client.assets.listCloud()
const runningEC2 = resources.filter(
r => r.provider === 'aws' && r.resourceType === 'AWS::EC2::Instance' && r.status === 'running'
)syncCloud(requestOpts?)
Enqueues an on-demand sync from all active cloud DataSource integrations.
await client.assets.syncCloud()
// Then poll status via client.dataSources.getSyncStatus(dataSourceId)Risk & Compliance Domain
RiskService — client.risk
listVulnerabilities(opts?, requestOpts?)
const { data: vulns } = await client.risk.listVulnerabilities({
severity: 'critical',
status: 'open',
source: 'qualys',
})getVulnerability(id, requestOpts?)
const vuln = await client.risk.getVulnerability('vuln-uuid')
// vuln.cveId, vuln.cvssScore, vuln.affectedAssetId, vuln.dueDateupdateVulnerabilityStatus(id, status, requestOpts?)
const updated = await client.risk.updateVulnerabilityStatus(
'vuln-uuid',
'in_remediation'
)listControls(framework?, requestOpts?)
// All controls
const { data: allControls } = await client.risk.listControls()
// SOC 2 controls only
const { data: soc2Controls } = await client.risk.listControls('soc2')
const gaps = soc2Controls.filter(c => c.status !== 'implemented' && c.status !== 'not_applicable')getControl(id, requestOpts?)
const control = await client.risk.getControl('control-uuid')
// control.framework, control.controlId, control.evidence, control.statuslistRisks(requestOpts?) / getRisk(id, requestOpts?)
const { data: risks } = await client.risk.listRisks()
const openHighRisks = risks.filter(r => r.status === 'open' && r.riskScore >= 15)createRisk(input, requestOpts?)
const risk = await client.risk.createRisk({
title: 'Unpatched critical CVEs in production SaaS tools',
description: 'Multiple production SaaS applications have critical CVEs that have not been addressed.',
category: 'security',
likelihood: 'likely',
impact: 'major',
owner: 'ciso-person-uuid',
linkedVulnerabilityIds: ['vuln-uuid-1', 'vuln-uuid-2'],
mitigationPlan: 'Engage vendors for patches; apply compensating controls in the interim.',
dueDate: '2026-07-01',
})Common Domain
RelationshipService — client.relationships
list(sourceId?, targetId?, requestOpts?)
// All relationships involving a person as source
const { data: rels } = await client.relationships.list('person-uuid')create(input, requestOpts?)
const rel = await client.relationships.create({
sourceId: 'person-uuid',
sourceType: 'Person',
targetId: 'asset-uuid',
targetType: 'TechnologyAsset',
relationshipType: 'owns',
confidence: 0.95,
source: 'manual',
})delete(id, requestOpts?)
await client.relationships.delete('relationship-uuid')traverse(entityId, entityType, depth?, requestOpts?)
Graph traversal — returns all entities connected within depth hops.
// Find everything connected to this person within 2 hops
const nodes = await client.relationships.traverse('person-uuid', 'Person', 2)
for (const node of nodes) {
console.log(`${node.entityType} ${node.entityId} at depth ${node.depth} via ${node.viaRelationship.relationshipType}`)
}DataSourceService — client.dataSources
list(requestOpts?)
const { data: sources } = await client.dataSources.list()
const errored = sources.filter(s => s.status === 'error')create(input, requestOpts?)
const source = await client.dataSources.create({
name: 'Production Okta',
type: 'okta',
config: {
domain: 'acme.okta.com',
apiToken: process.env.OKTA_API_TOKEN!,
},
})triggerSync(id, requestOpts?)
await client.dataSources.triggerSync('datasource-uuid')getSyncStatus(id, requestOpts?)
const status = await client.dataSources.getSyncStatus('datasource-uuid')
// status.running, status.entitiesProcessed, status.errors, status.completedAtError Handling
All SDK methods throw typed error classes that extend CpodError.
import {
CpodError,
ApiError,
AuthenticationError,
NotFoundError,
RateLimitError,
TimeoutError,
} from '@cpod/sdk'
try {
const person = await client.people.get('nonexistent-uuid')
} catch (err) {
if (err instanceof NotFoundError) {
console.error('Person not found:', err.message)
} else if (err instanceof AuthenticationError) {
console.error('Invalid API key or insufficient permissions')
} else if (err instanceof RateLimitError) {
console.error(`Rate limited. Retry after ${err.retryAfter} seconds`)
} else if (err instanceof TimeoutError) {
console.error('Request timed out')
} else if (err instanceof ApiError) {
console.error(`API error ${err.status}: ${err.message}`)
} else if (err instanceof CpodError) {
console.error('cPod SDK error:', err.message)
}
}The SDK automatically retries on transient errors (429, 500, 502, 503, 504) with exponential backoff up to 3 attempts.
Request Options
Every method accepts an optional RequestOptions argument as its last parameter.
interface RequestOptions {
/** AbortSignal for cancellation. */
signal?: AbortSignal
/** Override the client-level timeout in milliseconds. */
timeout?: number
/** Additional headers merged into this specific request. */
headers?: Record<string, string>
}Example — cancellable request with a custom timeout:
const controller = new AbortController()
setTimeout(() => controller.abort(), 5000)
const person = await client.people.get('person-uuid', {
signal: controller.signal,
timeout: 5000,
})