API Usage
The hosted CmdCal V2 API handles rendering, quality validation, brand compliance, and approval workflows. All endpoints live under https://cmdcal.com/api/v2/.
Today, that hosted runtime is presentation-focused. For XLSX generation, validation, repair, and template workflows, use the self-hosted @paperjsx/json-to-xlsx package or the CmdCal MCP spreadsheet tools.
Authentication
Every request requires an API key. Create one from your dashboard. Pass it as either:
- An
Authorization: Bearer <key>header - An
x-api-key: <key>header
If the key is invalid, the API returns 401. If the key has exceeded its monthly usage limit, the API returns 429.
The Canonical Flow
The V2 runtime follows a four-step loop:
- Preflight -- validate the document and get a quality report before committing to a render.
- Render -- submit the presentation document for PPTX generation (sync or async).
- Poll -- if async, poll the job until it completes.
- Review -- inspect artifacts, compare against baselines, and approve.
POST /api/v2/preflight
Validates a document against the quality and brand compliance contracts without producing an output file.
{
"sourceSchema": "protocol_v2",
"brandPackId": "bp_abc123",
"brandPackVersionId": "bpv_456",
"preflightPolicy": {
"autoFix": "off"
},
"document": {
"version": "2.0",
"title": "Board Update",
"slides": []
}
}
The response includes:
data.job_id-- a durable job record for this preflight run.data.job_url-- permalink to the job detail endpoint.data.quality_report-- the full quality report withdeckScore,findings, and per-slide reports.data.brand_compliance-- brand pack compliance details (when abrandPackIdis provided).data.policyResult-- the V2 guardrail decision.data.validationSummary-- what validation modes actually ran vs. were deferred.
policyResult
The policyResult object is the canonical guardrail decision:
{
allowedToRender: boolean;
blocking: boolean;
blockingCodes: string[];
blockingFindingCount: number;
unsupportedModes: string[];
reasons: string[];
nextActions: string[];
}
If blocking is true, the top-level status field is "blocked" instead of "success". If the requested validation mode is unsupported (e.g. desktop_blocking without an oracle backend), the endpoint returns a durable blocked decision rather than pretending the mode ran.
validationSummary
Records what ran and what was deferred:
requestedMode-- the mode you asked for.preflightExecutedModes/renderExecutedModes-- modes that actually ran in each phase.deferredModes/unsupportedModes-- modes that could not run.
POST /api/v2/render
Submits a document for rendering. Supports sync and async modes.
{
"sourceSchema": "protocol_v2",
"mode": "sync",
"approvalRequired": true,
"brandPackId": "bp_abc123",
"brandPackVersionId": "bpv_456",
"baselineJobId": "job_previous",
"preflightPolicy": {
"preflightOnly": false,
"renderIfScoreAbove": 60,
"autoFix": "off"
},
"document": {
"version": "2.0",
"title": "Board Update",
"slides": []
}
}
Sync mode ("mode": "sync" or omitted) blocks until the render completes and returns artifact URLs, quality_report, policyResult, and validationSummary inline.
Async mode ("mode": "async") returns immediately with HTTP 202:
{
"status": "queued",
"data": {
"job_id": "job_xyz",
"job_url": "https://cmdcal.com/api/v2/jobs/job_xyz"
}
}
Brand Pack Integration
Pass brandPackId and optionally brandPackVersionId to apply a brand pack template. The engine resolves the brand pack for your org, injects the template, and runs brand compliance checks alongside the quality report.
Approval Workflow
Set approvalRequired: true to mark the job as requiring approval before the artifact is released. The job status and approval history are available through GET /api/v2/jobs/:id.
Baseline Comparison
Pass baselineJobId referencing a previous render job to enable diff comparison. The diff is available at GET /api/v2/jobs/:id in the diffSummary field.
GET /api/v2/jobs/:id
Returns the full job record including:
job-- status, timestamps, document title, slide count, deck score.artifacts-- download URL, preview URL, validation record URL.approval-- current approval status and approval history.diffSummary-- deck-level diff against the baseline job.preflight-- preflight report summary.policy-- the policy decision that governed this job.findingSummary-- aggregated quality findings.brandCompliance-- brand compliance summary.validation-- structural and desktop validation results.baseline-- the baseline job's ID, status, title, and deck score.brandPack-- associated brand pack metadata.
GET /api/v2/jobs (list)
List jobs for your org with optional filters:
GET /api/v2/jobs?limit=25&status=succeeded&kind=render&brandPackId=bp_abc123
Supports filtering by status, approvalStatus, kind, brandPackId, and sourceSchema. Maximum limit is 100.
Rate Limits and Errors
401-- invalid or missing API key.429-- monthly usage limit exceeded.400-- invalid JSON body or document validation failure. The response includeserror.issueswith specific validation problems.412-- precondition failed (e.g. preflight policy blocked the render).422-- render failed. The response includeserror.codeanderror.message.500-- internal error. The response includes thejob_idso you can inspect the job record.
Complete Example
# 1. Preflight
curl -s -X POST https://cmdcal.com/api/v2/preflight \
-H "Authorization: Bearer pj_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"sourceSchema": "protocol_v2",
"document": {
"version": "2.0",
"title": "Q3 Board Update",
"slides": [
{ "slideType": "title-body", "title": "Summary", "body": ["Revenue grew 24% YoY."] },
{ "slideType": "kpi-grid", "title": "KPIs", "items": [
{ "label": "ARR", "value": "$12.4M", "trend": "up" }
]}
]
}
}' | jq '.data.policyResult.allowedToRender'
# 2. Render (sync)
RESULT=$(curl -s -X POST https://cmdcal.com/api/v2/render \
-H "Authorization: Bearer pj_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"sourceSchema": "protocol_v2",
"mode": "sync",
"document": {
"version": "2.0",
"title": "Q3 Board Update",
"slides": [
{ "slideType": "title-body", "title": "Summary", "body": ["Revenue grew 24% YoY."] },
{ "slideType": "kpi-grid", "title": "KPIs", "items": [
{ "label": "ARR", "value": "$12.4M", "trend": "up" }
]}
]
}
}')
JOB_ID=$(echo $RESULT | jq -r '.data.job_id')
DOWNLOAD=$(echo $RESULT | jq -r '.data.download_url')
# 3. Poll (only needed for async mode)
curl -s -H "Authorization: Bearer pj_live_your_key" \
"https://cmdcal.com/api/v2/jobs/$JOB_ID" | jq '.job.status'
# 4. Download
curl -o presentation.pptx "$DOWNLOAD"