{"openapi":"3.1.0","info":{"title":"Belbrein Ingestion API","version":"1.0.0","summary":"Send call-center conversations to Belbrein for scoring and coaching feedback.","description":"Belbrein scores call-center conversations against the quality criteria of the customer's industry and turns them into coaching feedback. This API is how a customer's own system delivers calls: one request per call, either the stereo recording or a speaker-labelled transcript, plus call metadata. Authenticate with the organisation's integration key, created by an admin on the Connector page in the Belbrein dashboard. Resending a call with the same `external_conversation_id` is always safe; it is never processed twice. Step-by-step guides (Dutch): https://www.belbrein.ai/docs","contact":{"name":"Belbrein","url":"https://www.belbrein.ai/#demo"}},"externalDocs":{"description":"Belbrein developer documentation (Dutch)","url":"https://www.belbrein.ai/docs"},"servers":[{"url":"https://app-callinsightswl-prod.azurewebsites.net","description":"Belbrein API"}],"security":[{"integrationKey":[]}],"tags":[{"name":"Ingestion","description":"Deliver calls for analysis."}],"paths":{"/v1/ingestion/conversations":{"post":{"operationId":"ingestTranscript","tags":["Ingestion"],"summary":"Send a call transcript","description":"Delivers one call as a speaker-labelled transcript plus metadata. The call is analysed before the response is sent, so a 200 means the work is done. Unknown fields are refused with a 400: names, phone numbers and e-mail addresses of the called party are deliberately not accepted.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptRequest"}}}},"responses":{"200":{"description":"Transcript received. `analysed` says whether it was scored; `reason` explains when it was not.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptAccepted"}}}},"400":{"description":"The request is invalid. `issues` names each field. Do not retry without changing the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Integration key missing or invalid. Check the `Authorization: Bearer <key>` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"This organisation is a demo environment and cannot receive real calls.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Request body larger than 10 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Error on our side. Retry with backoff (1 min, 5 min, 15 min, 1 h, 6 h).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Authentication temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ingestion/recordings":{"post":{"operationId":"ingestRecording","tags":["Ingestion"],"summary":"Send a call recording","description":"Delivers one call as a stereo audio file plus its metadata as multipart form fields. The recording is queued for transcription and analysis; poll `GET /v1/ingestion/recordings/{jobId}` with the returned `job_id` to follow it. Audio is deleted once transcribed.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/RecordingForm"}}}},"responses":{"202":{"description":"Recording accepted: `queued` with a `job_id`, or `skipped` when the agent is not linked yet or analysis is switched off (the call can be resent later).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingAccepted"}}}},"400":{"description":"The request is invalid (missing or empty audio, unsupported format, or a bad field). Do not retry without changing the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Integration key missing or invalid. Check the `Authorization: Bearer <key>` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"This organisation is a demo environment and cannot receive real calls.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A recording with this `external_conversation_id` was already accepted. Nothing to do.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Duplicate"}}}},"413":{"description":"Audio file larger than 500 MB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Error on our side. Retry with backoff (1 min, 5 min, 15 min, 1 h, 6 h).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Authentication temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/ingestion/recordings/{jobId}":{"get":{"operationId":"getRecordingJob","tags":["Ingestion"],"summary":"Get the status of a recording","description":"Returns the transcription and analysis status of a recording sent earlier. Only jobs of the organisation that owns the integration key are visible.","parameters":[{"name":"jobId","in":"path","required":true,"description":"The `job_id` returned when the recording was accepted.","schema":{"type":"string"}}],"responses":{"200":{"description":"Current status of the job.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingJob"}}}},"401":{"description":"Integration key missing or invalid. Check the `Authorization: Bearer <key>` header.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"This organisation is a demo environment and cannot receive real calls.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown job id for this organisation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Error on our side. Retry with backoff (1 min, 5 min, 15 min, 1 h, 6 h).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Authentication temporarily unavailable. Retry with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"integrationKey":{"type":"http","scheme":"bearer","description":"The organisation's integration key. Shown once when created; creating a new key invalidates the previous one."}},"schemas":{"TranscriptRequest":{"type":"object","properties":{"external_conversation_id":{"type":"string","minLength":1,"maxLength":200},"agent_external_id":{"type":"string","minLength":1,"maxLength":200},"conversation_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$"},"call_agent_name":{"type":"string","minLength":1,"maxLength":200},"campaign":{"type":"string","minLength":1,"maxLength":200},"outcome_code":{"type":"string","minLength":1,"maxLength":200},"outcome_category":{"type":"string","maxLength":200},"duration_seconds":{"type":"integer","minimum":0,"maximum":9007199254740991},"team_external_id":{"type":"string","maxLength":200},"contact_id":{"type":"string","maxLength":200},"metadata":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"transcript":{"minItems":1,"type":"array","items":{"type":"object","properties":{"speaker":{"type":"string","enum":["agent","customer"]},"start":{"type":"number","minimum":0},"end":{"type":"number","minimum":0},"text":{"type":"string","minLength":1},"confidence":{"type":"number","minimum":0,"maximum":1}},"required":["speaker","text"]}}},"required":["external_conversation_id","agent_external_id","conversation_date","call_agent_name","campaign","outcome_code","transcript"],"additionalProperties":false},"RecordingForm":{"type":"object","properties":{"audio":{"type":"string","format":"binary","description":"One stereo recording, agent and customer on separate channels. Accepted extensions: wav, mp3, flac, ogg, m4a. Maximum 500 MB."},"external_conversation_id":{"type":"string","minLength":1,"maxLength":200},"agent_external_id":{"type":"string","minLength":1,"maxLength":200},"conversation_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$"},"call_agent_name":{"type":"string","minLength":1,"maxLength":200},"campaign":{"type":"string","minLength":1,"maxLength":200},"outcome_code":{"type":"string","minLength":1,"maxLength":200},"outcome_category":{"type":"string","maxLength":200},"duration_seconds":{"type":"string","pattern":"^\\d+$","description":"Call duration in whole seconds."},"team_external_id":{"type":"string","maxLength":200},"contact_id":{"type":"string","maxLength":200},"metadata":{"type":"string","description":"Optional JSON object, sent as a string, with extra call fields. Never personal data of the called party."},"agent_channel":{"type":"string","enum":["0","1"],"description":"Which audio channel (0 = left, 1 = right) carries the agent."}},"required":["audio","external_conversation_id","agent_external_id","conversation_date","call_agent_name","campaign","outcome_code","agent_channel"],"additionalProperties":false},"TranscriptAccepted":{"type":"object","properties":{"status":{"type":"string","enum":["accepted"]},"external_conversation_id":{"type":"string"},"conversation_id":{"type":["string","null"]},"analysed":{"type":"boolean"},"reason":{"type":"string","description":"Why the call was not analysed, e.g. the agent is not linked yet."}},"required":["status","external_conversation_id","conversation_id","analysed"]},"RecordingAccepted":{"type":"object","properties":{"status":{"type":"string","enum":["queued","skipped"]},"job_id":{"type":"string","description":"Present when `status` is `queued`."},"reason":{"type":"string","enum":["agent_not_linked","analysis_disabled"],"description":"Present when `status` is `skipped`."},"external_conversation_id":{"type":"string"}},"required":["status","external_conversation_id"]},"RecordingJob":{"type":"object","properties":{"job_id":{"type":"string"},"external_conversation_id":{"type":"string"},"status":{"type":"string","enum":["queued","processing","transcribed","ingested","rejected","failed"]},"reject_reason":{"type":["string","null"],"enum":["not_stereo","too_short","undecodable","channel_silent","channel_bleed","channels_identical","channels_correlated","channels_not_separated",null]},"channel_separation":{"type":["string","null"]},"audio_seconds":{"type":["number","null"]},"conversation_id":{"type":["string","null"]},"transcript_retained_until":{"type":["string","null"],"format":"date-time"},"attempts":{"type":"integer"},"last_error":{"type":["string","null"]},"enqueued_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["job_id","external_conversation_id","status"]},"Duplicate":{"type":"object","properties":{"duplicate":{"type":"boolean","enum":[true]},"external_conversation_id":{"type":"string"},"message":{"type":"string"}},"required":["duplicate","external_conversation_id","message"]},"Error":{"type":"object","properties":{"error":{"type":"string","description":"What went wrong and, where it applies, what to do about it."}},"required":["error"]},"ValidationError":{"type":"object","properties":{"error":{"type":"string","enum":["Invalid payload."]},"issues":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}},"required":["field","message"]}}},"required":["error","issues"]}}}}