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:

  1. Preflight -- validate the document and get a quality report before committing to a render.
  2. Render -- submit the presentation document for PPTX generation (sync or async).
  3. Poll -- if async, poll the job until it completes.
  4. 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.

JSON
{
  "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 with deckScore, findings, and per-slide reports.
  • data.brand_compliance -- brand pack compliance details (when a brandPackId is 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:

TYPESCRIPT
{
  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.

JSON
{
  "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:

JSON
{
  "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:

Code
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 includes error.issues with specific validation problems.
  • 412 -- precondition failed (e.g. preflight policy blocked the render).
  • 422 -- render failed. The response includes error.code and error.message.
  • 500 -- internal error. The response includes the job_id so you can inspect the job record.

Complete Example

Terminal
# 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"