Developers

A simple REST API for your call-center data

Read-only endpoints for projects, audios and analyses. Available on the PRO and BUSINESS plans — pull data into your BI, CRM or internal dashboard.

Quick start

Three steps to your first API call:

  1. Subscribe to a PRO or BUSINESS plan from /billing.
  2. Generate an API key from /account/api-keys and copy it (shown once).
  3. Call the API with the key in the Authorization header.
curl https://ccr.confluent-digital.com/api/v1/projects \
  -H "Authorization: Bearer ccr_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Authentication

All endpoints require an API key passed in the Authorization header using the Bearer scheme. Keys are bound to one organization; revoking a key is immediate.

Authorization: Bearer ccr_live_<32 hex chars>

Lost a key? Revoke it from /account/api-keys and create a new one. The plain token is only visible at creation time — the server stores a SHA-256 hash.

Resources

Six endpoints, all read-only. Cross-organization access is blocked at the database level (a key from organization A receives 404 on a resource owned by organization B).

MethodPathDescription
GET/api/v1/projectsList projects (paginated).
GET/api/v1/projects/{id}Single project detail.
GET/api/v1/audiosList audio files. Filter by projectId and/or status.
GET/api/v1/audios/{id}Single audio detail. Includes the linked analysisId if any.
GET/api/v1/analysesList analyses. Filter by projectId and a createdAt range (from / to).
GET/api/v1/analyses/{id}Single analysis with criteria, recommendations and transcript. Raw Edith payload is intentionally not exposed.

Pagination

All list endpoints use the same page/limit envelope. limit max is 100, default 50.

GET /api/v1/audios?page=2&limit=50&projectId=<id>&status=DONE

200 OK
{
  "data": [
    {
      "id": "ckxxx...",
      "projectId": "ckyyy...",
      "originalName": "call_001.mp3",
      "size": 2483920,
      "source": "UPLOAD",
      "status": "DONE",
      "errorMessage": null,
      "createdAt": "2026-05-10T14:32:11.000Z",
      "processedAt": "2026-05-10T14:33:02.000Z",
      "analysisId": "ckzzz..."
    }
  ],
  "pagination": { "page": 2, "limit": 50, "total": 137 }
}

Filters

  • projectId scope to a single project (cuid).
  • status audio status: PENDING, PROCESSING, DONE or FAILED.
  • from, to inclusive bounds on createdAt, ISO-8601 strings.

Webhooks (BUSINESS plan)

Subscribe an HTTPS endpoint to platform events. We POST signed JSON to your URL whenever a subscribed event fires — no polling required. Manage endpoints at /account/webhooks (owner only, BUSINESS plan).

Events

MethodPathDescription
analysis.completedPOSTFires after an audio finished analysis successfully.
analysis.failedPOSTFires when an audio analysis terminates with a failure.
alert.createdPOSTFires when one of your alert rules matches a new analysis.
webhook.pingPOSTSynthetic test event triggered by the test button on /account/webhooks.

Payload format

All deliveries share the same envelope: an envelope with id, event, created_at and an event-specific data block. The HTTP body is the JSON below; headers carry the event name, the delivery id and the HMAC signature.

POST https://yourapp.example.com/webhooks/ccr
Content-Type: application/json
User-Agent: CallCenterRate-Webhook/1.0
X-CCR-Event: analysis.completed
X-CCR-Delivery-Id: ckdelivery123...
X-CCR-Signature: t=1715423112,v1=8f3a...c9d2

{
  "id": "ckdelivery123...",
  "event": "analysis.completed",
  "created_at": "2026-05-11T12:25:12.000Z",
  "data": {
    "analysis_id": "ckanalysis456...",
    "audio_file_id": "ckaudio789...",
    "project_id": "ckproject000...",
    "overall_score": 78.5
  }
}

Signature verification

We sign every body with HMAC-SHA256 using your signing secret. The X-CCR-Signature header has the form t=[unix-timestamp],v1=[hex]. Compute HMAC(secret, timestamp + "." + rawBody) and constant-time compare. Reject requests where the timestamp drifts more than 300 seconds — that prevents replay attacks.

// Node.js — vérifier la signature reçue
import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const [tPart, v1Part] = header.split(",");
  const timestamp = tPart.split("=")[1];
  const signature = v1Part.split("=")[1];
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(signature, "hex"),
  );
}

Retry & delivery guarantees

Deliveries are queued and retried with exponential backoff on transport errors and on any HTTP response that is not 2xx. We retry up to 6 times across about 24 hours.

  • Backoff: 0s, 1min, 5min, 30min, 2h, 6h, 24h.
  • We follow no redirects and reject private IPs (SSRF guard) and any non-HTTPS URL.
  • Total delivery timeout per attempt is 10 seconds. Your endpoint should respond fast and process asynchronously.

Errors

All errors return a JSON body with a stable error code. The HTTP status reflects the error class.

{
  "error": {
    "code": "plan_required",
    "message": "API access requires the PRO or BUSINESS plan."
  }
}
HTTPcodeDescription
401unauthorizedAPI key is missing, malformed, unknown or revoked.
403plan_requiredThe organization is not on a plan that grants API access. Upgrade to PRO or BUSINESS.
429rate_limit_exceededMonthly API call quota exceeded. Wait for the period reset (header `Retry-After` in seconds) or upgrade the plan.
404not_foundThe resource does not exist or belongs to another organization.
400validation_errorQuery parameter or body validation failed. The message details which field.

Quota & rate limiting

Each plan has a monthly API call quota (PRO: 10,000 / BUSINESS: 100,000). The counter resets on the same cycle as the analyses quota. On overuse, the API returns 429 with a `Retry-After` header (seconds until reset). Per-minute burst limits will be added in a future revision.

HTTP/1.1 429 Too Many Requests
Retry-After: 1209600
Content-Type: application/json

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Monthly API call quota exceeded (10000/10000). Resets on 2026-06-01T00:00:00.000Z."
  }
}