Bolcho

Core resources

Recordings

Upload your own call recordings — your team's calls, not your assistant's — and get the same analysis Bolcho runs on its own calls: sentiment, summary, key points, concerns, a lead score and any structured fields you have defined. Upload a batch, poll it while it processes, then read the aggregate or drill into a single call.

Recordings are not calls. A call is something an assistant did; an uploaded recording is something a person did. They are separate resources so that call counts, costs and durations stay unambiguous — nothing you upload here appears in GET /calls or in your call metrics.

How a batch runs

POST /recordings/batches accepts up to 500 audio files (200 MB each) and returns immediately. Each recording moves through queued → transcribing → analyzing → done, three at a time. A 200-file batch takes minutes: poll GET /recordings/batches/{id} and watch doneCount, failedCount and completedAt.

Two ways to say what to extract

Point at an assistant. Pass agentId and the batch uses that assistant's published Analysis configuration — the same structured schema, summary instruction and success rubric it applies to its own calls. structuredData and successEvaluation then mean exactly the same thing on an upload as on a call.

Or send a template inline. If your extraction lives in your own product rather than on a Bolcho assistant, pass analysisConfig — a JSON object with any of summaryPrompt, structuredSchema (JSON Schema) and outcomes (the labels to classify into, each with a description of what it means). Outcome labels come back on outcome; they answer *what happened*, where a success rubric answers *did it work*, so the two are separate fields and either may be absent.

agentId wins if you send both — one template per batch, never two competing definitions. Send neither and you get summary, sentiment, key points, concerns, next steps and a lead score.

json
{
  "summaryPrompt": "what the patient asked for and how it was left",
  "outcomes": [
    { "name": "Booked", "description": "The customer confirmed a slot." },
    { "name": "Call back later", "description": "Asked to be contacted again." },
    { "name": "Not interested" }
  ],
  "structuredSchema": {
    "type": "object",
    "properties": {
      "package": { "type": "string", "description": "Which test package" },
      "preferredDay": { "type": "string", "enum": ["Sat", "Sun"] }
    }
  }
}

Failures are normal, and re-runnable

Corrupt files, silent recordings and unsupported codecs are expected in a bulk upload. Each one is stored with status: "failed" and a plain-English failureReason rather than failing the batch. POST /recordings/batches/{id}/retry re-queues every failed recording.

Cost: transcription is billed per minute of audio and analysis per call, both deducted from your credit balance as each recording completes. A batch is rejected up front if your balance cannot cover the estimate, and stops mid-way if the balance runs out — the recordings that did not run say so in failureReason.

Matching a recording to a customer

If the filename contains a phone number — the usual 9876543210_2026-08-04.mp3 shape phone systems export — the digits are stored on phone and matched against your existing customers by their last 10 digits, so a bare local number in a filename still finds the +91… contact a real call created. On a match, contactId is set. A recording never *creates* a customer: a filename cannot tell us the country code, and a guessed one would split a single person across two records.

POST/recordings/batches calls:write

Upload a batch

Upload audio files as multipart/form-data under the field name `files` (repeat it once per file). Returns the created batch; processing starts immediately in the background.

Body

Send as multipart/form-data , repeating the files field once per file.

Body

files*file[]Audio files. Up to 500 per batch, 200 MB each. MP3/WAV/M4A/OGG and MP4/WebM audio.
namestringBatch name, e.g. "July sales calls". Defaults to a count and date.
agentIduuidApply this assistant's Analysis configuration — its structured schema, summary instruction and success rubric.
analysisConfigstringA JSON analysis template, for callers with no assistant to point at: `{ summaryPrompt?, structuredSchema?, outcomes? }`. A string because this is a multipart request. Ignored when `agentId` is given.
bash
curl -X POST https://api.bolchoai.in/v1/recordings/batches -H "Authorization: Bearer $BOLCHO_API_KEY" \
  -F "name=July sales calls" \
  -F "agentId=7c9e…" \
  -F "files=@call-9876543210.mp3" \
  -F "files=@call-9812345678.mp3"

Response

The batch, with an empty aggregate — nothing has been analysed yet.

json
{
  "id": "b1f3…",
  "name": "July sales calls",
  "agentId": "7c9e…",
  "totalCount": 2,
  "doneCount": 0,
  "failedCount": 0,
  "analysed": 0,
  "estimatedCostUsd": "0.0182",
  "costUsd": "0",
  "completedAt": null,
  "createdAt": "2026-08-04T09:12:44.000Z",
  "aggregate": { "sentiment": { "positive": 0, "neutral": 0, "negative": 0 }, "avgBantScore": null, "totalDurationSec": 0, "topConcerns": [] }
}
GET/recordings/batches calls:read

List batches

Your upload batches, newest first.

Query parameters

limitnumberPage size (default 20).
offsetnumberRows to skip.
bash
curl https://api.bolchoai.in/v1/recordings/batches -H "Authorization: Bearer $BOLCHO_API_KEY"

Response

json
{
  "data": [ { "id": "b1f3…", "name": "July sales calls", "totalCount": 200, "doneCount": 187, "failedCount": 13, "costUsd": "0.8412", "completedAt": "2026-08-04T09:31:02.000Z", "createdAt": "2026-08-04T09:12:44.000Z" } ],
  "total": 1,
  "limit": 20,
  "offset": 0
}
GET/recordings/batches/{id} calls:read

Get a batch

One batch with its aggregate across every analysed recording — the sentiment split, average lead score, total audio analysed, and the concerns raised most often. Poll this while `completedAt` is null.

Path parameters

id*uuidBatch id.
bash
curl https://api.bolchoai.in/v1/recordings/batches/b1f3… -H "Authorization: Bearer $BOLCHO_API_KEY"

Response

json
{
  "id": "b1f3…",
  "name": "July sales calls",
  "totalCount": 200,
  "doneCount": 187,
  "failedCount": 13,
  "analysed": 187,
  "estimatedCostUsd": "0.9100",
  "costUsd": "0.8412",
  "completedAt": "2026-08-04T09:31:02.000Z",
  "aggregate": {
    "sentiment": { "positive": 96, "neutral": 61, "negative": 30 },
    "avgBantScore": 54,
    "totalDurationSec": 91240,
    "topConcerns": [ { "concern": "price is too high", "count": 22 }, { "concern": "no weekend slots", "count": 14 } ]
  }
}
GET/recordings/batches/{id}/recordings calls:read

List recordings in a batch

The individual recordings, oldest first. Filter to review what failed, or to read only the negative calls.

Path parameters

id*uuidBatch id.

Query parameters

statusstring`queued`, `transcribing`, `analyzing`, `done` or `failed`.
sentimentstring`positive`, `neutral` or `negative`.
limitnumberPage size (default 20).
offsetnumberRows to skip.
bash
curl "https://api.bolchoai.in/v1/recordings/batches/b1f3…/recordings?sentiment=negative" -H "Authorization: Bearer $BOLCHO_API_KEY"

Response

json
{
  "data": [
    { "id": "r7a2…", "originalName": "call-9876543210.mp3", "status": "done", "durationSec": 412, "sentiment": "negative", "buyerIntent": "low", "bantScore": 21, "summary": "Customer asked about a full-body package and left when told the weekend slots were full.", "successEvaluation": false, "outcome": "Not interested", "phone": "9876543210", "contactId": "c4d1…", "failureReason": null },
    { "id": "r7a9…", "originalName": "hold-music.mp3", "status": "failed", "failureReason": "No speech was detected in this recording.", "sentiment": null }
  ],
  "total": 30,
  "limit": 20,
  "offset": 0
}
GET/recordings/{id} calls:read

Get a recording

One recording in full: the speaker-labelled transcript, the whole analysis, any extracted fields, and a short-lived URL to play the audio.

Path parameters

id*uuidRecording id.
bash
curl https://api.bolchoai.in/v1/recordings/r7a2… -H "Authorization: Bearer $BOLCHO_API_KEY"

Response

json
{
  "id": "r7a2…",
  "batchId": "b1f3…",
  "originalName": "call-9876543210.mp3",
  "status": "done",
  "durationSec": 412,
  "language": "hi",
  "transcript": "Speaker 0: Bilal Clinic Lab, good morning.\nSpeaker 1: Haan, full body package ka rate kya hai?",
  "summary": "Customer asked about a full-body package and left when told the weekend slots were full.",
  "sentiment": "negative",
  "buyerIntent": "low",
  "keyPoints": [ "Asked for full-body package pricing", "Wanted a Sunday slot" ],
  "concerns": [ "no weekend slots" ],
  "nextSteps": "Call back when Sunday collection slots open and offer the ₹1,499 package.",
  "successEvaluation": false,
  "outcome": "Not interested",
  "bantScore": 21,
  "structuredData": { "package": "full body", "preferredDay": "Sunday" },
  "phone": "9876543210",
  "contactId": "c4d1…",
  "audioUrl": "https://…blob.core.windows.net/…?sig=…",
  "transcriptionCostUsd": "0.0295",
  "analysisCostUsd": "0.0004",
  "analyzedAt": "2026-08-04T09:18:10.000Z",
  "createdAt": "2026-08-04T09:12:45.000Z"
}
POST/recordings/batches/{id}/retry calls:write

Retry failed recordings

Re-queue every recording in the batch that failed, and start processing again. Returns how many were re-queued.

Path parameters

id*uuidBatch id.
bash
curl -X POST https://api.bolchoai.in/v1/recordings/batches/b1f3…/retry -H "Authorization: Bearer $BOLCHO_API_KEY"

Response

json
{ "requeued": 13 }
DELETE/recordings/batches/{id} calls:write

Delete a batch

Delete a batch and every analysis in it. The uploaded audio itself is retained.

Path parameters

id*uuidBatch id.
bash
curl -X DELETE https://api.bolchoai.in/v1/recordings/batches/b1f3… -H "Authorization: Bearer $BOLCHO_API_KEY"

Response

json
{ "ok": true }
Bolcho — Voice AI for Bharat