> ## Documentation Index
> Fetch the complete documentation index at: https://docs.60db.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Click-to-Call

> State, outcome, cost and recording status for one click-to-call

Read one click-to-call. `{id}` accepts either the **`call_id`** or your own **`reference_id`**, so you can reconcile straight from your own records without storing ours.

## Request

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token with your API key
</ParamField>

### Path

<ParamField path="id" type="string" required>
  The `call_id` or the `reference_id`
</ParamField>

### Query

<ParamField query="refresh" type="boolean" default="false">
  Ask the call platform for the authoritative state before answering. Accepts `1`, `true` or `yes`.

  Off by default so a UI can poll this cheaply. Turn it on when you need the truth — it is what repairs a call whose terminal webhook never arrived, and what picks up a recording that has finished uploading. It only goes upstream when the call is unfinished, or finished-but-awaiting-recording; an upstream blip never fails the read.
</ParamField>

## Response

**200 OK** with `data.call` — the same object [Place Click-to-Call](/api-reference/click2call/place-call) returns.

Every field below lives under `data.call`. The ones worth reading first:

<ResponseField name="state" type="string">
  `initiated` (dialling), `bridged` (talking), `completed`, `failed`, `rejected` or `unknown`
</ResponseField>

<ResponseField name="call_done" type="boolean">
  `true` once the outcome is final — this is the flag to gate your own state machine on
</ResponseField>

<ResponseField name="terminal_event" type="string | null">
  How it ended: `call.completed`, `call.not_answered`, `call.failed` or `call.temporaryfailed`
</ResponseField>

<ResponseField name="hangup_cause" type="string | null">
  Why: `NORMAL_CLEARING`, `NO_ANSWER`, `USER_BUSY`, `CALL_REJECTED`, `MAX_DURATION`, `ORIGINATOR_CANCEL`, `UNALLOCATED_NUMBER`, `NORMAL_CIRCUIT_CONGESTION`, `MEDIA_FAILURE`, `CARRIER_LINK_DOWN`
</ResponseField>

<ResponseField name="leg" type="string | null">
  On failures: `agent` or `customer` — which side failed
</ResponseField>

<ResponseField name="talk_seconds" type="integer | null">
  Bridged talk time. This is what is billed
</ResponseField>

<ResponseField name="total_seconds" type="integer | null">
  Seconds from admission to the end of the call
</ResponseField>

<ResponseField name="cost_usd" type="number | null">
  What this call cost. `null` with `billing_status: null` means nothing was owed
</ResponseField>

<ResponseField name="recording_available" type="boolean">
  `true` when [a playback URL](/api-reference/click2call/recording-url) can be minted
</ResponseField>

## Errors

| Status | Code                     | Meaning                                     |
| ------ | ------------------------ | ------------------------------------------- |
| 404    | `CALL_NOT_FOUND`         | No such call or reference in this workspace |
| 409    | `DIALER_NOT_PROVISIONED` | Trunk not set up (Gate T)                   |

## Example

<RequestExample>
  ```bash cURL theme={null}
  # By call id, with an authoritative refresh
  curl "https://api.60db.ai/dialer/click2call/7d004ba2-565f-4ee2-9aa2-19523d8db7ae?refresh=1" \
    -H "Authorization: Bearer your-api-key"

  # Or straight from your own reference
  curl "https://api.60db.ai/dialer/click2call/crm-task-91823" \
    -H "Authorization: Bearer your-api-key"
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch(
    `https://api.60db.ai/dialer/click2call/${callId}?refresh=1`,
    { headers: { Authorization: `Bearer ${process.env.SIXTYDB_API_KEY}` } },
  );
  const { data } = await res.json();

  if (data.call.call_done) {
    console.log(data.call.terminal_event, data.call.talk_seconds, data.call.cost_usd);
  }
  ```

  ```python Python theme={null}
  res = requests.get(
      f"https://api.60db.ai/dialer/click2call/{call_id}",
      params={"refresh": 1},
      headers={"Authorization": f"Bearer {os.environ['SIXTYDB_API_KEY']}"},
  )
  call = res.json()["data"]["call"]
  print(call["state"], call["terminal_event"], call["talk_seconds"])
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "success": true,
    "data": {
      "call": {
        "call_id": "7d004ba2-565f-4ee2-9aa2-19523d8db7ae",
        "reference_id": "crm-task-91823",
        "outbound_trunk_id": "00000000-0000-4000-8000-000000000000",
        "did": "+917900000000",
        "agent_number": "+919812345678",
        "customer_number": "+919876543210",
        "state": "completed",
        "call_done": true,
        "terminal_event": "call.completed",
        "hangup_cause": "NORMAL_CLEARING",
        "leg": null,
        "error": null,
        "error_detail": null,
        "talk_seconds": 66,
        "total_seconds": 92,
        "start_time": "2026-09-23T14:11:27.134Z",
        "answer_time": "2026-09-23T14:11:53.392Z",
        "end_time": "2026-09-23T14:12:59.921Z",
        "metadata": { "lead_id": "L-991" },
        "record_requested": true,
        "recording_id": "rec_01J9Z4K7",
        "recording_status": "uploaded",
        "recording_available": true,
        "callback_url": "https://your-app.example.com/webhooks/qcall",
        "callback_secret_last4": "oose",
        "cost_usd": 0.0033,
        "billing_status": "billed",
        "placed_via": "api_key",
        "created_by_user_id": 4021,
        "api_key_id": 17,
        "api_key_name": "crm-integration",
        "created_at": "2026-09-23T14:11:27.134Z"
      }
    }
  }
  ```

  ```json 404 Not found theme={null}
  {
    "success": false,
    "message": "That call does not belong to this workspace.",
    "code": "CALL_NOT_FOUND"
  }
  ```
</ResponseExample>
