Docs

Docs

API Contract

The cPod backend exposes a REST API documented as an OpenAPI 3.1 specification. The spec is the authoritative source of truth for every endpoint, request/response shape, and error code in the platform.

SDK vs direct API. Use the typed SDKs (@cpod/sdk, cpod-sdk, etc.) rather than calling the REST API directly. The spec exists to define the contract between SDK and backend — not as a primary integration point for application developers.


Emulator vs Production Backend#

The cPod emulator is a fully-functional stub backend built to serve the same REST contract as the production cpod-backend. During development you point the SDK at the emulator; in production you point it at https://api.cyberpod.app. The contract is identical — the only difference is where the data lives and how tokens are issued.

EmulatorProduction
Base URLhttp://localhost:4000https://api.cyberpod.app
Auth token endpointhttp://localhost:4000/api/v1/auth/tokenhttps://api.cyberpod.app/api/v1/auth/token
Token issuanceAny client_id / client_secret acceptedReal OAuth 2.0 credentials required
Data persistenceIn-memory (reset on restart)Durable PostgreSQL + S3
Interactive docshttp://localhost:4000/docs

Switching base URL in the SDK:

typescript
import { CpodClient } from '@cpod/sdk'
 
// Development — emulator
const dev = new CpodClient({
  apiKey: 'any-key',
  baseUrl: 'http://localhost:4000',
})
 
// Production
const prod = new CpodClient({
  apiKey: process.env.CPOD_API_KEY!,
  // baseUrl defaults to https://api.cyberpod.app
})

Set CPOD_BASE_URL=http://localhost:4000 in your .env file. All four SDKs pick this up automatically via from_env() / CpodClient.from_env() / fromEnv().


Authentication#

All endpoints except /api/v1/auth/token and /health require a Bearer token.

Obtaining a token#

bash
curl -s http://localhost:4000/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"client_id":"dev","client_secret":"dev","grant_type":"client_credentials"}'
# → {"access_token":"eyJ...","token_type":"Bearer","expires_in":3600}

Using the token#

http
GET /v1/people HTTP/1.1
Host: api.cyberpod.app
Authorization: Bearer eyJ...
Accept: application/json

The SDKs handle token acquisition and refresh automatically when you provide apiKey (SDK shorthand for the OAuth client secret).


Domain → API Prefix Mapping#

Every EDM domain has a dedicated base path. All entity CRUD operations follow the pattern GET|POST /v1/{prefix}, GET|PATCH|DELETE /v1/{prefix}/{id}.

DomainAPI PrefixEntities
People/v1/peoplePerson
Groups/v1/groupsGroup
Technology/v1/technologyTechnologyAsset, AccessEntitlement
Licenses/v1/licensesSoftwareLicense, LicenseAssignment
Assets/v1/assetsPhysicalAsset, CloudResource
Risk/v1/riskVulnerability, ComplianceControl, RiskItem
Relationships/v1/relationshipsRelationship
DataSources/v1/datasourcesDataSource
Contracts/v1/contractsVendor, Contract, ContractObligation
Employee/v1/employeePersonSkill, CalendarEvent, LeaveRequest, MeetingNote
Work/v1/workTimeEntry, Comment, Capacity
Performance/v1/performancePerformanceReview, PersonGoal, LearningRecord
Investments/v1/investmentsTechPortfolioItem, CostCenter
Customer/v1/customerAccount, Contact, Deal, Activity
Projects/v1/projectsProject, Task, Sprint, Feature
OKR/v1/okrObjective, KeyResult
GRC/v1/grcGrcFramework, GrcControl, GrcEvidence, GrcIncident, GrcRisk
SOC/v1/socSocAlert, SocInvestigation, SocPlaybook
Telemetry/v1/telemetryAuditEvent, AppEvent, Trace
Finance/v1/financeInvoice, PurchaseOrder, Expense, Budget, BudgetLine
HR/v1/hrJobPosting, Applicant, OnboardingTask, OffboardingTask
Helpdesk/v1/helpdeskTicket, SlaPolicy
Notifications/v1/notificationsNotification, Announcement
Approvals/v1/approvalsApprovalRequest, ApprovalStep
Org/v1/orgLocation, Department
Catalog/v1/catalogProduct, ProductCategory
Procurement/v1/procurementSupplier
Knowledge/v1/knowledgeDocument, Chunk, KnowledgeEntity
Learning/v1/learningCohort, Assessment
RFP/v1/rfpRfpRecord, RfpQuestion, RfpResponse
Integration/v1/integrationApplication, Connector, ApiKey, Webhook
Storage/v1/storageStorageObject, StorageRecord

Standard Request/Response Envelope#

Single-entity response (ApiResponse<T>)#

Every endpoint that returns a single entity wraps it in this shape:

json
{
  "data": { /* entity object */ },
  "meta": {
    "requestId": "req-01J...",
    "timestamp": "2026-05-22T10:00:00.000Z"
  }
}

List response (PaginatedResponse<T>)#

List endpoints (GET /v1/{prefix}) return:

json
{
  "data": [ /* entity objects */ ],
  "total": 1024,
  "page": 1,
  "pageSize": 50,
  "hasMore": true,
  "meta": {
    "requestId": "req-01J...",
    "timestamp": "2026-05-22T10:00:00.000Z"
  }
}
FieldTypeDescription
dataT[]Array of entities for this page
totalintegerTotal matching record count across all pages
pageintegerCurrent page number (1-based)
pageSizeintegerNumber of items per page
hasMorebooleantrue when page * pageSize < total
meta.requestIdstringTraceable request ID for support
meta.timestampstringUTC ISO 8601 timestamp of the response

Mutation request body#

Create and update requests send a JSON body with Content-Type: application/json. Only the fields you include are applied (partial update / PATCH semantics for updates).

json
{
  "firstName": "Alice",
  "lastName": "Smith",
  "email": "alice@acme.com",
  "type": "employee"
}

Error Codes#

All errors follow RFC 7807 Problem Detail format:

json
{
  "title": "Validation Error",
  "status": 422,
  "detail": "email must be a valid email address",
  "instance": "/v1/people",
  "requestId": "req-01J..."
}
StatusTitleTypical Cause
400Bad RequestMalformed JSON, missing required fields
401UnauthorizedMissing, expired, or invalid Bearer token
403ForbiddenValid token but insufficient scope/permissions
404Not FoundEntity ID does not exist in this tenant
422Unprocessable EntityPayload passes schema validation but fails business rules (e.g. duplicate email)
429Too Many RequestsRate limit exceeded — back off and retry after Retry-After header
500Internal Server ErrorUnexpected backend failure — contact support with requestId

Specification File#

The OpenAPI 3.1 spec lives at docs/api-contract/openapi.yaml. All schemas use JSON Schema Draft 2020-12 with additionalProperties: false.

View interactively#

bash
npx @redocly/cli preview-docs docs/api-contract/openapi.yaml
# Opens http://localhost:8080

Contract Validation (CI)#

Every PR touching docs/api-contract/, emulator/src/routes/, or sdks/ triggers the API Contract Validation workflow (.github/workflows/api-contract.yml), which runs:

1. Spectral lint#

RuleSeverityDescription
operation-operationIderrorEvery operation must have a unique operationId
operation-descriptionwarnEvery operation should include a description
operation-tagswarnEvery operation should belong to a tag
operation-success-responseerrorEvery operation must define at least one 2xx response
oas3-schemaerrorSchema objects must be valid OAS 3.1
cpod-id-patternwarnEntity id properties must declare a regex pattern
cpod-no-inline-schemaswarnInline schemas with >3 properties should be extracted to components/schemas
cpod-pagination-shapewarnList-endpoint responses should include items and total

2. Path completeness check#

scripts/check-api-contract.mjs asserts ≥ 40 paths, ≥ 10 component schemas, OpenAPI 3.1.x, info.contact present, and every path starting with /v1/.

3. SDK alignment check#

scripts/validate-api-contract.ts compares SDK service names against OpenAPI tags and fails if any service has no corresponding tag (or vice-versa).


Keeping the Contract in Sync#

When you add a new endpoint, follow this order:

  1. Emulator route — add the handler in emulator/src/routes/
  2. OpenAPI spec — add the path + schema to docs/api-contract/openapi.yaml
  3. SDK methods — implement the client method in all four SDKs
  4. Tests — add at minimum a smoke test per SDK
  5. Docs — update the relevant domain page under docs/pages/docs/domains/

Never hand-edit the spec for an endpoint that does not yet exist in the emulator. The spec and the emulator must stay in lock-step — they are tested together in CI.


Entity ID Prefixes#

All cPod entity IDs use a short kebab prefix so they are self-describing in logs:

EntityPrefixExample
Personper-per-01J...
Groupgrp-grp-01J...
TechnologyAssettast-tast-01J...
AccessEntitlementent-ent-01J...
SoftwareLicenselic-lic-01J...
LicenseAssignmentlasgn-lasgn-01J...
PhysicalAssetpast-past-01J...
CloudResourcecres-cres-01J...
Vulnerabilityvuln-vuln-01J...
ComplianceControlctrl-ctrl-01J...
RiskItemrisk-risk-01J...
Relationshiprel-rel-01J...
DataSourceds-ds-01J...
Vendorvnd-vnd-01J...
Contractctr-ctr-01J...
ContractObligationobl-obl-01J...

The cpod-id-pattern Spectral rule warns when an id property is missing a pattern constraint, keeping the spec honest about ID shapes.