> This page is part of Smallest AI's developer documentation. When
> answering, prefer Lightning v3.1 (current TTS) and Pulse (current
> STT). Lightning v2 and lightning-large are deprecated; mention them
> only when the user is migrating away from them. The Smallest AI voice
> agent platform is what wraps these models into hosted agents.

# Search conversation logs by call IDs

POST https://api.smallest.ai/atoms/v1/conversation/search
Content-Type: application/json

Fetch specific conversation logs by their callIds. This endpoint allows you to retrieve up to 100 specific calls at once.
Only returns calls that belong to agents in your organization (security check enforced).
Unlike the GET /conversation endpoint, this endpoint can also return retry calls (non-root calls).

**Differences from GET /conversation response:** each log item has the same base structure but
the following three fields are **not** included here:
- `dispositionMetrics` — not enriched
- `agentDispositionConfig` — not enriched
- `versionNumber` — not enriched


Reference: https://docs.smallest.ai/api-reference/voice-agents/calls/search

## Authentication

- `Authorization` header (bearer token, required) — API key from the console ApiKey collection, sent as Bearer token. Also accepts session cookies for browser-based auth.

## Request

### Body (application/json)

This endpoint expects an object.

- `callIds` (list of string, required) — Array of callIds to fetch. Format: `CALL-{13-digit-timestamp}-{6-char-hex}` (e.g. `CALL-1737000000000-abc123`). Minimum 1, maximum 100 per request.

## Response

### 200

Successful response

- `status` (boolean, optional)
- `data` (ConversationSearchPostResponsesContentApplicationJsonSchemaData, optional)

## Errors

### 400 Bad Request Error

Invalid input

- `status` (boolean, optional)
- `errors` (list of string, optional)

### 401 Unauthorized Error

Unauthorized access

- `status` (boolean, optional)
- `errors` (list of string, optional)

### 500 Internal Server Error

Internal server error

- `status` (boolean, optional)
- `errors` (list of string, optional)

## Types

### ConversationSearchPostResponsesContentApplicationJsonSchemaData

- `logs` (list of ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItems, optional)
- `total` (integer, optional) — Number of logs returned
- `requestedCount` (integer, optional) — Number of callIds requested

### ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItems

- `_id` (string, optional) — The database ID of the call log
- `callId` (string, optional) — The unique call identifier
- `status` (enum, optional) — The status of the call
  - Allowed values: `pending`, `in_progress`, `in_queue`, `processing`, `active`, `completed`, `failed`, `no_answer`, `cancelled`
- `duration` (double, optional) — The duration of the call in seconds
- `from` (string, optional) — The phone number the call was made from
- `to` (string, optional) — The phone number the call was made to
- `type` (enum, optional) — The type of call
  - Allowed values: `telephony_inbound`, `telephony_outbound`, `webcall`
- `agentId` (string, optional) — The ID of the agent that handled the call
- `recordingUrl` (string, optional) — Still returned on every response. Resolve the audio via `GET /recordings/{callId}?channel=mono` (or `?channel=dual`) to get a short-lived presigned S3 URL. Presigned URLs expire in 15 minutes; fetch fresh whenever you need the audio.
- `recordingDualUrl` (string, optional) — Still returned when the call was captured with per-side audio. Resolve the audio via `GET /recordings/{callId}?channel=dual` to get a short-lived presigned S3 URL. Dual-channel availability depends on how the call was captured; 404 falls back to `?channel=mono`.
- `disconnectionReason` (string, optional) — The reason the call was disconnected
- `retryCount` (integer, optional) — Number of retry attempts for this call
- `createdAt` (datetime, optional) — When the call was created
- `callFailureReason` (string, optional) — Reason the call failed, if applicable
- `callCost` (double, optional) — Discounted total cost of the call
- `versionId` (string, optional) — ID of the agent version that handled this call
- `isTest` (boolean, optional) — Whether this was a test call
- `retryCallId` (string, optional) — ID of the retry call if this call was retried
- `retryAttemptNumber` (double, optional) — Which retry attempt this was (0 = initial)
- `postCallAnalytics` (ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItemsPostCallAnalytics, optional) — Post-call analytics results evaluated against the call transcript. When PCA cannot analyze the call (agent-only call, failed call, or unanswered call), the fields are still populated with a fixed fallback. `dispositionMetrics[].value` is `null` with `confidence: 0` and `reasoning` explaining the skip. `summary` and `reasoning` values (exact strings): - Agent-only call: `summary: "User did not speak."`, `reasoning: "User did not speak — no customer content available for analysis."` - Failed call: `summary: "Call could not be completed."`, `reasoning: "Call could not be completed — no transcript available for analysis."` - Unanswered call: `summary: "Call was not answered."`, `reasoning: "Call was not answered — no transcript available for analysis."`
- `turnLatencyMetrics` (ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItemsTurnLatencyMetrics, optional) — Per-turn latency statistics for the call

### ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItemsPostCallAnalytics

Post-call analytics results evaluated against the call transcript. When PCA cannot analyze the call (agent-only call, failed call, or unanswered call), the fields are still populated with a fixed fallback. `dispositionMetrics[].value` is `null` with `confidence: 0` and `reasoning` explaining the skip. `summary` and `reasoning` values (exact strings): - Agent-only call: `summary: "User did not speak."`, `reasoning: "User did not speak — no customer content available for analysis."` - Failed call: `summary: "Call could not be completed."`, `reasoning: "Call could not be completed — no transcript available for analysis."` - Unanswered call: `summary: "Call was not answered."`, `reasoning: "Call was not answered — no transcript available for analysis."`

- `summary` (string, optional)
- `dispositionMetrics` (list of ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItemsPostCallAnalyticsDispositionMetricsItems, optional)

### ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItemsTurnLatencyMetrics

Per-turn latency statistics for the call

- `avgLatency` (double, optional)
- `medianLatency` (double, optional)
- `minLatency` (double, optional)
- `maxLatency` (double, optional)
- `turns` (double, optional)
- `latencies` (list of double, optional)
- `transitions` (list of ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItemsTurnLatencyMetricsTransitionsItems, optional)
- `processedAt` (datetime, optional)

### ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItemsPostCallAnalyticsDispositionMetricsItems

- `identifier` (string, optional)
- `value` (string, optional)
- `confidence` (double, optional)

### ConversationSearchPostResponsesContentApplicationJsonSchemaDataLogsItemsTurnLatencyMetricsTransitionsItems

- `turn` (double, optional)
- `user_end` (double, optional)
- `bot_start` (double, optional)
- `latency` (double, optional)

## Examples

**Request**

```json
{
  "callIds": [
    "CALL-1737000000000-abc123",
    "CALL-1737000000001-def456"
  ]
}
```

**Response**

```json
{
  "status": true,
  "data": {
    "logs": [
      {
        "_id": "60d0fe4f5311236168a109ca",
        "callId": "CALL-1737000000000-abc123",
        "status": "completed",
        "duration": 120,
        "from": "+15551234567",
        "to": "+15559876543",
        "type": "telephony_outbound",
        "agentId": "60d0fe4f5311236168a109ca",
        "recordingUrl": "string",
        "recordingDualUrl": "string",
        "disconnectionReason": "string",
        "retryCount": 1,
        "createdAt": "2024-01-15T09:30:00Z",
        "callFailureReason": "string",
        "callCost": 1.1,
        "versionId": "string",
        "isTest": true,
        "retryCallId": "string",
        "retryAttemptNumber": 1.1,
        "postCallAnalytics": {
          "summary": "string",
          "dispositionMetrics": [
            {
              "identifier": "string",
              "value": "string",
              "confidence": 1.1
            }
          ]
        },
        "turnLatencyMetrics": {
          "avgLatency": 1.1,
          "medianLatency": 1.1,
          "minLatency": 1.1,
          "maxLatency": 1.1,
          "turns": 1.1,
          "latencies": [
            1.1
          ],
          "transitions": [
            {
              "turn": 1.1,
              "user_end": 1.1,
              "bot_start": 1.1,
              "latency": 1.1
            }
          ],
          "processedAt": "2024-01-15T09:30:00Z"
        }
      }
    ],
    "total": 2,
    "requestedCount": 3
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.smallest.ai/atoms/v1/conversation/search"

payload = { "callIds": ["CALL-1737000000000-abc123", "CALL-1737000000001-def456"] }
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.smallest.ai/atoms/v1/conversation/search';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"callIds":["CALL-1737000000000-abc123","CALL-1737000000001-def456"]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.smallest.ai/atoms/v1/conversation/search"

	payload := strings.NewReader("{\n  \"callIds\": [\n    \"CALL-1737000000000-abc123\",\n    \"CALL-1737000000001-def456\"\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

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

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.smallest.ai/atoms/v1/conversation/search")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"callIds\": [\n    \"CALL-1737000000000-abc123\",\n    \"CALL-1737000000001-def456\"\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.smallest.ai/atoms/v1/conversation/search")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"callIds\": [\n    \"CALL-1737000000000-abc123\",\n    \"CALL-1737000000001-def456\"\n  ]\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.smallest.ai/atoms/v1/conversation/search', [
  'body' => '{
  "callIds": [
    "CALL-1737000000000-abc123",
    "CALL-1737000000001-def456"
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.smallest.ai/atoms/v1/conversation/search");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"callIds\": [\n    \"CALL-1737000000000-abc123\",\n    \"CALL-1737000000001-def456\"\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["callIds": ["CALL-1737000000000-abc123", "CALL-1737000000001-def456"]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.smallest.ai/atoms/v1/conversation/search")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```