Public API

Call Trace API Documentation

Submit call recordings from your own system, receive an immediate 202 response, and get the final transcript and evaluation through signed webhooks.

Create an API key

Open Admin > Integrations and create a key with the analysis:write scope.

Submit a file URL

Send an HTTPS fileUrl to POST /api/v1/analyses. The API responds with 202 Accepted.

Receive the result

Subscribe to analysis.completed and analysis.failed to get the final evaluation.

Verify the signature

Validate X-CallTrace-Signature before accepting any webhook payload.

API keys

Create a token in the admin panel

Only admins can create integration API keys. Keys are scoped, can expire, can be revoked, and are independent from employee sessions.

1

Open Integrations

Sign in as an admin and open Admin > Integrations > API Keys.

2

Name the key

Use a name that identifies the external system, environment, and owner.

3

Choose scopes

Select only the permissions that system needs. Most upload integrations need analysis:write and analysis:read.

4

Set lifetime

Choose an expiration date for temporary access or leave it permanent for long-running service integrations.

5

Copy once

The full ct_live_ token is shown only once. After closing the panel, only the prefix remains visible.

Token format

ct_live_8Lk3...full-secret-value

Store the full token in your external system secret manager. Call Trace stores only a hash and shows only the prefix after creation.

Copy it immediately

The token is displayed only once when it is created. If it is lost, revoke that key and create a new one. Do not send API keys through email, chat, or browser console logs.

Authentication

Use a scoped integration key

Integration API keys are separate from employee sessions. Send the key in the Authorization header and grant only the scopes your system needs.

Header

Authorization: Bearer ct_live_...
Content-Type: application/json

Scopes

analysis:write creates analyses.

analysis:read reads status and results.

webhooks:write manages webhook settings.

Permissions

What each scope allows

Scopes are additive. Start with the smallest set that supports the integration and add more only when a specific endpoint requires it.

Scope
Use
Description
analysis:write
Create new analyses
Required for POST /api/v1/analyses. Allows the external system to submit a fileUrl and queue analysis.
analysis:read
Read analysis status and result
Required for GET /api/v1/analyses and GET /api/v1/analyses/{analysisId}. Use it for polling and reconciliation.
calls:read
Read call-level data
Allows access to call metadata exposed through integration-safe endpoints. Do not grant it if the integration only sends files.
storage:write
Write source files
Reserved for integrations that need direct storage upload flows. fileUrl integrations usually do not need it.
webhooks:read
Read webhook configuration and history
Allows listing webhook endpoints and delivery attempts for support or monitoring tooling.
webhooks:write
Manage webhooks
Allows creating or updating webhook endpoints. Keep it on admin/service keys, not ordinary analysis submission keys.

Recommended minimum

For the standard flow, create one token with analysis:write and analysis:read. Webhooks are configured by an admin in the UI, so the external sender usually does not need webhooks:write.

Token lifetime

Expiration, permanent access, and rotation

Each API key has its own lifetime. A token can be permanent, expire automatically, or be revoked manually.

Permanent token

expiresAt is empty. The token stays active until an admin revokes it. Use this only for trusted backend-to-backend integrations with a rotation process.

Expiring token

expiresAt is set. After that date the API returns 401 and no new requests are accepted. Use this for tests, contractors, or temporary access.

Revoked token

revokedAt is set immediately when an admin revokes the key. Revocation is instant and cannot be undone; create a new key instead.

Rotation

Create a new key, deploy it in the external system, confirm last used updates, then revoke the old key. This avoids downtime.

API reference

Create an analysis

The request stores the source file and starts background processing. Your client should not wait for transcription or evaluation to finish in this request.

Create analysis

Returns 202 Accepted and queues processing.

POST/api/v1/analyses

Request

POST /api/v1/analyses
Authorization: Bearer ct_live_...
Content-Type: application/json

{
  "fileUrl": "https://example.com/calls/call-123.mp3",
  "fileName": "call-123.mp3",
  "language": "auto",
  "externalId": "crm-call-123",
  "idempotencyKey": "crm-call-123",
  "metadata": {
    "customerId": "cust_42",
    "source": "crm"
  }
}

Response

HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "analysisId": "6f6af4bb-4f23-4f3f-a87e-85f2f8322dc1",
  "status": "queued",
  "externalId": "crm-call-123"
}
Field
Type
Required
Description
fileUrl
string
Required
HTTPS URL of the audio file to analyze.
fileName
string
Optional
Original filename shown in Call Trace.
agentId
uuid
Optional
Agent to associate with the analysis.
queueId
uuid
Optional
Queue or script context used for evaluation.
language
string
Optional
Language hint. Use auto when the source is unknown.
externalId
string
Optional
Your CRM, helpdesk, or telephony identifier.
idempotencyKey
string
Optional
Deduplicates retries from your system.
metadata
object
Optional
Small JSON object returned in status and webhook payloads.

File URL requirements

fileUrl must use HTTPS. Private, localhost, and internal IP ranges are rejected, including redirects into private networks. Files are downloaded with timeouts, content-type checks, and maximum size limits before processing.

API reference

Read status and results

Use this endpoint when your system needs to reconcile state or recover after missed webhook deliveries.

Get analysis

Returns the latest job status and available result fields.

GET/api/v1/analyses/{analysisId}
{
  "analysisId": "6f6af4bb-4f23-4f3f-a87e-85f2f8322dc1",
  "status": "completed",
  "externalId": "crm-call-123",
  "call": {
    "durationSeconds": 184,
    "agentId": "agent-uuid",
    "queueId": "queue-uuid"
  },
  "evaluation": {
    "score": 82,
    "summary": "The agent followed the script and resolved the main request.",
    "criteria": [
      { "name": "Greeting", "score": 10, "maxScore": 10 },
      { "name": "Need discovery", "score": 18, "maxScore": 20 }
    ]
  },
  "transcript": {
    "language": "en",
    "segments": [
      { "speaker": "agent", "text": "Good afternoon, how can I help?" }
    ]
  },
  "metadata": {
    "customerId": "cust_42"
  },
  "error": null
}
queued

The request is accepted and waiting for processing.

processing

The file is being downloaded, transcribed, and evaluated.

completed

The evaluation, transcript, and call summary are available.

failed

Processing failed after retries. Details are returned in error.

API reference

List analyses

Search by status, externalId, agent, queue, or date range. Use this for back-office reconciliation and support tooling.

List analyses

Returns paginated analyses for the integration company.

GET/api/v1/analyses
GET /api/v1/analyses?status=completed&externalId=crm-call-123
Authorization: Bearer ct_live_...

Webhooks

Webhook payload

Configure webhook endpoints in Admin > Integrations. Payloads include the information needed to store the assessment result in your own system.

analysis.createdanalysis.completedanalysis.failed
{
  "deliveryId": "whd_01jz8n0f8ck2bk0smq7h2x1t0r",
  "event": "analysis.completed",
  "occurredAt": "2026-06-19T12:00:00.000Z",
  "analysisId": "6f6af4bb-4f23-4f3f-a87e-85f2f8322dc1",
  "externalId": "crm-call-123",
  "call": {
    "status": "completed",
    "durationSeconds": 184,
    "recordingUrl": "https://storage.example.com/audio/call-123.mp3"
  },
  "evaluation": {
    "score": 82,
    "summary": "The agent followed the script and resolved the main request.",
    "criteria": [
      { "name": "Greeting", "score": 10, "maxScore": 10 }
    ]
  },
  "transcript": {
    "language": "en",
    "segments": [
      { "speaker": "agent", "text": "Good afternoon, how can I help?" }
    ]
  },
  "metadata": {
    "customerId": "cust_42"
  },
  "error": null
}

Script text is not included by default. Use deliveryId for deduplication and analysisId plus event when storing state transitions.

Webhooks

Delivery and retries

Call Trace stores every delivery attempt and retries failed deliveries so results are not lost on timeout or temporary customer outages.

Success

Any 2xx response marks the delivery as delivered.

Retry policy

Initial attempt plus 3 retries with backoff, for example 10 seconds, 1 minute, and 5 minutes.

History

Each attempt stores status, response code, error message, timestamps, and payload snapshot.

Webhooks

Verify signatures

Every webhook is signed with the endpoint secret shown once when the endpoint is created.

Headers

X-CallTrace-Timestamp

X-CallTrace-Delivery

X-CallTrace-Signature

The signed value is timestamp + "." + raw request body.

const expected = crypto
  .createHmac("sha256", process.env.CALL_TRACE_WEBHOOK_SECRET)
  .update(timestamp + "." + rawRequestBody)
  .digest("hex");

const valid = signature === "sha256=" + expected;

Examples

Create an analysis from your language

All examples use the same stable endpoint and expect the API key in CALL_TRACE_API_KEY.

JavaScript

const response = await fetch("https://your-domain.com/api/v1/analyses", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.CALL_TRACE_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    fileUrl: "https://example.com/calls/call-123.mp3",
    fileName: "call-123.mp3",
    language: "auto",
    externalId: "crm-call-123",
    idempotencyKey: "crm-call-123"
  })
});

const analysis = await response.json();
console.log(analysis.analysisId);

Go

payload := strings.NewReader(`{
  "fileUrl": "https://example.com/calls/call-123.mp3",
  "fileName": "call-123.mp3",
  "language": "auto",
  "externalId": "crm-call-123",
  "idempotencyKey": "crm-call-123"
}`)

req, err := http.NewRequest("POST", "https://your-domain.com/api/v1/analyses", payload)
if err != nil {
  log.Fatal(err)
}

req.Header.Set("Authorization", "Bearer "+os.Getenv("CALL_TRACE_API_KEY"))
req.Header.Set("Content-Type", "application/json")

res, err := http.DefaultClient.Do(req)

Java

HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://your-domain.com/api/v1/analyses"))
  .header("Authorization", "Bearer " + System.getenv("CALL_TRACE_API_KEY"))
  .header("Content-Type", "application/json")
  .POST(HttpRequest.BodyPublishers.ofString("""
    {
      "fileUrl": "https://example.com/calls/call-123.mp3",
      "fileName": "call-123.mp3",
      "language": "auto",
      "externalId": "crm-call-123",
      "idempotencyKey": "crm-call-123"
    }
  """))
  .build();

HttpResponse<String> response = HttpClient.newHttpClient()
  .send(request, HttpResponse.BodyHandlers.ofString());

PHP

$payload = [
  "fileUrl" => "https://example.com/calls/call-123.mp3",
  "fileName" => "call-123.mp3",
  "language" => "auto",
  "externalId" => "crm-call-123",
  "idempotencyKey" => "crm-call-123"
];

$ch = curl_init("https://your-domain.com/api/v1/analyses");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer " . getenv("CALL_TRACE_API_KEY"),
    "Content-Type: application/json"
  ],
  CURLOPT_POSTFIELDS => json_encode($payload),
  CURLOPT_RETURNTRANSFER => true
]);

$response = curl_exec($ch);

Errors

Error handling

Use idempotency keys for safe retries. Validation errors are returned synchronously. Processing errors are returned by status endpoint and analysis.failed webhooks.

400

Invalid JSON, missing fileUrl, unsupported content type, or invalid metadata.

401

Missing, expired, revoked, or malformed API key.

403

The API key does not include the required scope.

409

The same idempotency key was already used for another payload.

422

The file URL is blocked, private, not HTTPS, too large, or not reachable.

500

Temporary server error. Retry with the same idempotency key.