Upload Recording
curl --request POST \
--url https://api.example.com/recordings/add/{appointmentId} \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"source": "<string>"
}
'import requests
url = "https://api.example.com/recordings/add/{appointmentId}"
payload = { "source": "<string>" }
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({source: '<string>'})
};
fetch('https://api.example.com/recordings/add/{appointmentId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/recordings/add/{appointmentId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'source' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/recordings/add/{appointmentId}"
payload := strings.NewReader("{\n \"source\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/recordings/add/{appointmentId}")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"source\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/recordings/add/{appointmentId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"_id": "<string>",
"appointment_id": "<string>",
"recording_url": "<string>",
"recording_key": "<string>",
"append_recordings": [
{}
],
"file_size_bytes": 123,
"createdAt": "<string>",
"updatedAt": "<string>"
}
}Recordings
Upload Recording
Upload an audio recording file for a specific appointment.
POST
/
recordings
/
add
/
{appointmentId}
Upload Recording
curl --request POST \
--url https://api.example.com/recordings/add/{appointmentId} \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"source": "<string>"
}
'import requests
url = "https://api.example.com/recordings/add/{appointmentId}"
payload = { "source": "<string>" }
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({source: '<string>'})
};
fetch('https://api.example.com/recordings/add/{appointmentId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/recordings/add/{appointmentId}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'source' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/recordings/add/{appointmentId}"
payload := strings.NewReader("{\n \"source\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/recordings/add/{appointmentId}")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"source\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/recordings/add/{appointmentId}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"source\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"_id": "<string>",
"appointment_id": "<string>",
"recording_url": "<string>",
"recording_key": "<string>",
"append_recordings": [
{}
],
"file_size_bytes": 123,
"createdAt": "<string>",
"updatedAt": "<string>"
}
}Overview
Uploads an audio recording file for a specific appointment. The file is stored securely and the appointment is moved into thetranscribing state so that transcription begins automatically.
Only one recording may exist per appointment. If a recording already exists, the upload is rejected. You must be the appointment’s owning doctor to upload a recording.
Only one recording can be uploaded per appointment. If a recording already exists, the upload is rejected with
Recording already exists.Authentication
string
required
Bearer JWT token. The calling user is derived from the token; there is no
uid parameter.Authorization: Bearer <JWT>
Path Parameters
string
required
The appointment ObjectId to associate the recording with. The caller must be this appointment’s owning doctor.
665f8a1b2c3d4e5f6789012f3
Body Parameters
The request body must bemultipart/form-data.
file
required
The audio recording file to upload. Maximum size 500 MB.
string
default:"live"
Origin of the recording. Accepted values:
live (default), upload (a pre-recorded audio file), and online_meeting. The source determines which plan feature is required; if your plan does not include the relevant capability the request is rejected with a 403 FEATURE_NOT_AVAILABLE response.Response
boolean
required
Indicates whether the recording was uploaded successfully.
object
The created recording record.
Show Recording Object
Show Recording Object
string
Unique recording identifier.
string
The associated appointment ObjectId.
string
Internal storage reference for the stored recording. Not a fetchable link. Treat as opaque — its format is not part of the API contract.
string
Storage key of the recording file.
array
Additional recording segments attached to this recording. Empty on a fresh upload.
number
Size of the uploaded file in bytes. May be
null.string
Creation timestamp (ISO 8601).
string
Last-update timestamp (ISO 8601).
Additional fields (for example
duration_seconds, merged_recording, replaced_recording, __v) may appear on the recording object. Treat any field not documented here as opaque.Example Request
curl -X POST \
'https://app.medisync.me/api/recordings/add/665f8a1b2c3d4e5f6789012f3' \
-H 'Authorization: Bearer <JWT>' \
-F 'audio=@recording.wav'
const formData = new FormData();
formData.append('audio', audioFile);
const response = await fetch(
'https://app.medisync.me/api/recordings/add/665f8a1b2c3d4e5f6789012f3',
{
method: 'POST',
headers: {
'Authorization': 'Bearer <JWT>'
},
body: formData
}
);
const result = await response.json();
import requests
files = {'audio': open('recording.wav', 'rb')}
headers = {'Authorization': 'Bearer <JWT>'}
response = requests.post(
'https://app.medisync.me/api/recordings/add/665f8a1b2c3d4e5f6789012f3',
headers=headers,
files=files
)
result = response.json()
Example Response
Success (200)
{
"success": true,
"data": {
"_id": "665f8a1b2c3d4e5f6789012f4",
"appointment_id": "665f8a1b2c3d4e5f6789012f3",
"recording_url": "<internal storage reference>",
"recording_key": "<internal storage reference>",
"append_recordings": [],
"file_size_bytes": 4831200,
"createdAt": "2026-06-30T10:30:00.000Z",
"updatedAt": "2026-06-30T10:30:00.000Z"
}
}
Error Responses
401 Unauthorized
{
"success": false,
"message": "Unauthorized"
}
403 Feature Not Available
{
"success": false,
"code": "FEATURE_NOT_AVAILABLE",
"feature": "transcription.audio_upload",
"message": "This feature is not included in your current plan."
}
403 Forbidden (not the owning doctor)
{
"success": false,
"error": "Forbidden"
}
400 No File Uploaded
{
"success": false,
"error": "No file uploaded"
}
400 Recording Already Exists
{
"success": false,
"error": "Recording already exists"
}
The
Unauthorized response uses the message field, whereas handler-level errors use the error field. A session ended on another device returns 401 { "success": false, "message": "Session ended on this device because you signed in elsewhere.", "code": "SESSION_REVOKED" }.Behavior Notes
- One recording per appointment: A second upload for the same appointment is rejected with
Recording already exists. - Ownership: Only the appointment’s owning doctor can upload a recording; otherwise the request returns
403 Forbidden. - Automatic transcription: On success the appointment status is set to
transcribingand transcription begins automatically. When transcription completes the status advances toprocessing. - File size limit: Uploads larger than 500 MB are rejected.