Skip to main content
CMDashCybersecurity Maturity Dashboard
DocsDevelopersDevelopers

Developers

ROADMAP-V71 B5. One page that answers "can I integrate this?" in a minute — the front door for the 8 files this directory already had no index for. Every claim below is sourced from the files it links to; none of it is restated prose that could drift from them.

Public API v2 quickstart

Base URL: https://app.cmdash.net/api (self-hosted: your own origin). Auth: Authorization: Bearer cmdash_ek_... — mint a key at Settings → API Keys (Enterprise plan, Owner or Admin role). Full auth, scope and rate-limit detail: public-api-versioning.md.

Read (GET /v2/posture/latest, scope posture:read):

bash
curl https://app.cmdash.net/api/v2/posture/latest \
  -H "Authorization: Bearer cmdash_ek_..."
json
{
  "success": true,
  "requestId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-03-31T12:00:00.000Z",
  "data": {
    "score": 74,
    "grade": "C",
    "delta": 3,
    "computedAt": "2026-03-31T12:00:00.000Z",
    "signals": []
  }
}

Write (POST /v2/risks, scope risks:write), with an idempotency key so a retried request never double-creates:

bash
curl -X POST https://app.cmdash.net/api/v2/risks \
  -H "Authorization: Bearer cmdash_ek_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: r9f7d2c4-e1a8-44b3-9c7e-3f8b1e9d0a2c" \
  -d '{"title":"Unpatched critical vulnerabilities in production","likelihood":4,"impact":5,"category":"Vulnerability Management"}'
json
{
  "success": true,
  "requestId": "9b1f0e2a-6c4d-4e3a-8f2b-1d7c5a9e0f3b",
  "timestamp": "2026-03-31T12:00:01.000Z",
  "data": {
    "id": "risk-uuid-001",
    "riskId": "RSK-001",
    "title": "Unpatched critical vulnerabilities in production",
    "likelihood": 4,
    "impact": 5,
    "riskScore": 20,
    "status": "OPEN"
  }
}

Every route, scope and rate limit, generated from the controllers: public-api-reference.md. What v1 promises and why v2 exists: public-api-versioning.md. A vCISO or MSP practice reading the client workspaces it owns uses a PARTNER key: partner-api.md.

What is capped, how a limit answers, and what a webhook does and does not guarantee: limits.md.

Try it against the sandbox

ROADMAP-V80 H7. You do not need an account to make the first call. https://demo.cmdash.net is a public, read-only workspace full of sample data, rebuilt from the seed every night. Mint yourself a key, then use it:

bash
CMDASH_SANDBOX_KEY=$(curl -s -X POST https://demo.cmdash.net/api/sandbox/key | jq -r .data.key)

curl https://demo.cmdash.net/api/v2/posture/latest \
  -H "Authorization: Bearer $CMDASH_SANDBOX_KEY"

(.data.key, not .key — like every other CMDash API response, this one is wrapped in the standard { success, requestId, timestamp, data } envelope.)

ROADMAP-V100 I5. POST /sandbox/key is anonymous, rate-limited (the same per-IP/hour ceiling as POST /auth/sandbox-session), and mints a fresh key scoped to the sandbox tenant only, carrying every :read scope the Public API defines. It expires in 24 hours (or at the next nightly sandbox reset, whichever comes first) — mint another one when it does, the same way. Like any CMDash API key, it is shown once, in the response above; there is no way to look its raw value up again afterward, self-serve-minted or not.

If you would rather not mint your own — a fixed value to paste into a CI example, say — a second, static, never-expiring sandbox key is published the same way it always has been: minted by docs/runbooks/sandbox-first-run.md Step 2 and kept in the ops secrets vault, ask whoever provisioned the sandbox for its value. Both keys work identically against the sandbox tenant; only POST /sandbox/key is self-serve. Swap demo.cmdash.net for app.cmdash.net and your own tenant's key (Settings → API Keys) when you move to real data — nothing else in the request changes.

What the sandbox key can and cannot do, and why:

ReadsEvery GET the Public API defines, on the sandbox tenant only. It carries every :read scope.
WritesRefused, always — 403 SANDBOX_READ_ONLY, on every POST/PUT/PATCH/DELETE. Not "it has no write scope": the refusal is a global guard that runs before any route, so it does not depend on which scopes the key happens to hold.
Exports and downloadsRefused, including GET ones (/v2/audit-logs/export, /data-export/*, …/download). A public unauthenticated bulk-export endpoint is a free data-egress and PDF-rendering surface even when the data behind it is fake.
Other tenantsInvisible. sandbox:read is a restriction on top of tenant isolation, never a replacement for it — the sandbox key resolves to the sandbox tenant and RLS does the rest.
LifetimeIt does not expire, and it survives the nightly reset. The workspace behind it does not: everything you can see there is regenerated from the seed each night.

The rule is a single check applied identically to browser sandbox sessions and to this key, and it is tested route by route against the whole API, not a sample.

API documentation formats:

  • Download OpenAPI (JSON) — Import into any OpenAPI-compatible tool. Covers both v1 and v2 in one document, tagged v1 <resource> / v2 <resource> (v1 operations are also marked "deprecated": true) — the same v2 routes shown in the quickstart above are in here.
  • Browse the API reference — Every v1 and v2 operation, rendered in-site from the same document above (ROADMAP-V90 A6 — no more sending readers to petstore.swagger.io).
  • Run in Postman — Download and import into Postman, or use Postman's import feature with the JSON file. Same v1+v2 coverage, one folder per resource — v2 folders first, the deprecated v1 folders after them (ROADMAP-V100 I3).

SDK

@cmdash/api-sdk — a hand-written, zero-dependency TypeScript client. client.<namespace> is v2, the current version (ROADMAP-V100 I4); client.v2.<namespace> is the same object spelled out, and the deprecated v1 surface is at client.v1.<namespace>. Kept honest by a test that fails CI if a route and an SDK method ever disagree, for either version. Install and first call, copied verbatim from the package's own README:

typescript
import { CmdashClient } from '@cmdash/api-sdk';

const client = new CmdashClient({
  apiKey: process.env.CMDASH_API_KEY!,
  // baseUrl: 'https://app.cmdash.net/api', // default; override for on-prem
});

const posture = await client.posture.getLatest();
console.log(posture?.score, posture?.grade);

The full README, including error types and CI-pipeline guidance, is published with the package on npm.

Python: cmdash-api-sdk — generated from this same served spec (/docs/openapi.json), zero dependencies, fully type-hinted. Install, auth, three examples and how it's regenerated: sdk-python.md.

Webhooks

CMDash pushes events to you: register a URL and events at POST /v2/webhooks, verify each delivery's X-CMDash-Signature (HMAC-SHA256, 5-minute replay window), and deduplicate on eventId — nine event types, sequencing/gap-detection guarantees, retry/circuit-breaker and rotation semantics: webhook-events.md.

Building the receiving end for a vendor pushing events into CMDash (GitHub, Okta, Jira, ServiceNow, CrowdStrike) is a different guide, for a different direction of traffic: webhook-receivers.md.

Connector development + conformance kit

A connector you contribute is a declarative JSON definition (an HTTP request template + JSONPath field mappings) — never compiled code running inside CMDash's process. Author it as your own custom connector, validate the shape offline, then submit it for review. Full walkthrough, the definition schema, and the security boundary this design enforces: connector-development.md.

Status: usable today, no network required. The kit is a zero-dependency script — verified in this checkout:

bash
node docs/developers/connector-conformance-kit/validate.mjs \
  docs/developers/connector-conformance-kit/examples/valid-example.json

reports pass/fail plus the exact reason for each failure (a credential-shaped header, an unsupported JSONPath filter, a non-frozen observation type, a duplicate target field on the invalid fixture). Details: connector-conformance-kit/README.md.

Terraform provider

terraform-provider-cmdash manages what the public API v2 can actually write today — full CRUD is only real for cmdash_webhook_registration (GET/POST/PUT/DELETE /v2/webhooks); frameworks and integrations are read-only data sources; policy, custom roles and alert rules have no public API route at all yet, so those three resources are not implemented rather than faked.

Status: builds clean from this checkout (cd terraform-provider-cmdash && go build . — verified). Unit tests need no network; acceptance tests run against an in-process HTTP fake, gated by TF_ACC=1. The full resource matrix, gap list, local dev_overrides setup and testing instructions: terraform-provider.md.

Idempotency

Every POST/PUT/PATCH/DELETE on the public API accepts an Idempotency-Key header (Stripe's model): same key + same body within 24h returns the exact cached response instead of re-running the side effect; same key with a different body is a 422; a key already in flight is a 409. Format, server-side state machine, and the client retry pattern: idempotency.md. Uploads are covered too: the fingerprint includes the file's bytes, so a retry that keeps the form and swaps the file is a 422, not the first file's cached response.

List conventions

Every internal list endpoint takes ?limit&offset&sort&dir&q and answers with { items, totalCount, hasMore, limit, offset }. Six competing conventions were measured before this; the older spellings (page, pageSize, search, sortBy, sortDir) are still accepted, resolved onto the canonical ones, so no saved URL breaks. The public API (/v1, /v2) is exempt by design — it is versioned, SDK-consumed and under an active Sunset contract: list-conventions.md.

Optimistic concurrency

The internal API's four long-form editors (risk, policy metadata, report template, custom maturity model) return a strong ETag on their single-entity read and honour If-Match on the save: a stale write is refused with 412 Precondition Failed (details.code PRECONDITION_FAILED) and nothing is written, while a request that sends no If-Match is unaffected. Which routes, how the token is derived, why the check is atomic rather than advisory, and the client retry pattern: optimistic-concurrency.md.

Data export format

POST /data-export (OWNER only) produces a full-fidelity JSON archive of every tenant-scoped model, generated from the live Prisma schema so a new model is included by default rather than by an edit someone has to remember. Paired with a deletion certificate on offboarding that states exactly what was executed and what it does not claim: data-export-format.md.

Versioning

/v1/* is deprecated (not removed) as of 2026-08-24, Sunset 2027-02-24; /v2/* is the same auth, rate limits and envelope with two 200 { error } endpoints fixed to real 404s and six previously-unenforced scopes wired up. What "breaking" means here, the full deprecation-header contract, and the SDK's v1 targeting: public-api-versioning.md.

Self-hosting

CMDash runs on your own hardware or in your own AWS account, on the same code the hosted service runs.

  • Your own Docker host: self-host.md covers the install, preflight and first-login path. The install is licensed by a signed license.json (a 30-day self-host trial is one per account and watermarks the install as an evaluation).
  • Your own AWS account: self-host-aws.md covers three CloudFormation templates in deploy/aws/ (evaluation, production with a load balancer and RDS, and GovCloud) that launch the CMDash AMI. The Licensing parameter chooses the entitlement source once, at launch: license-file (a signed CMDash licence file you upload in the app) or marketplace (the plan comes from your AWS Marketplace subscription, read through AWS License Manager).
  • Licence verification is entirely offline for a licence file, so a self-hosted deployment never has to contact CMDash to check one.
  • Air-gapped installation: the distributable bundle ships 3 of the 9 images a full stack needs, and an unsigned bundle is rejected by default, so use the connected install paths above for a fresh host.

Community

ROADMAP-V80 J4. Questions about the SDK or the Terraform provider, real-time chat, and office hours: cmdash.com/community.

This page is the product manual, published from the repository at docs/developers/README.md. Customers read the same text inside CMDash under Help. Last updated October 10, 2026.

Developers — CMDash Docs