> 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 ", "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 ', '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 ") 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 ' 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 response = Unirest.post("https://api.smallest.ai/atoms/v1/conversation/search") .header("Authorization", "Bearer ") .header("Content-Type", "application/json") .body("{\n \"callIds\": [\n \"CALL-1737000000000-abc123\",\n \"CALL-1737000000001-def456\"\n ]\n}") .asString(); ``` ```php request('POST', 'https://api.smallest.ai/atoms/v1/conversation/search', [ 'body' => '{ "callIds": [ "CALL-1737000000000-abc123", "CALL-1737000000001-def456" ] }', 'headers' => [ 'Authorization' => 'Bearer ', '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 "); 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 ", "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() ```