@eka-care/ekascribe-ts-sdk 3.0.23 → 3.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +763 -1220
- package/dist/index.d.ts +19 -3
- package/dist/index.mjs +4348 -2920
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,29 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# EkaScribe TypeScript SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
The Eka Care Ekascribe SDK allows you to capture and process audio, generating structured medical documentation using Eka Care's voice transcription API.
|
|
8
|
-
|
|
9
|
-
## Documentation
|
|
10
|
-
|
|
11
|
-
[Visit the documentation site](https://developer.eka.care/api-reference/health-ai/ekascribe/SDKs/TS-sdk)
|
|
3
|
+
Browser SDK for capturing audio and generating structured medical documentation using Eka Care's voice transcription API.
|
|
12
4
|
|
|
13
5
|
## Prerequisites
|
|
14
6
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- Node 14 or higher
|
|
18
|
-
- `npm` or `yarn` for dependency management
|
|
19
|
-
- Access and refresh tokens from Eka Care (optional for some methods)
|
|
7
|
+
- Node 14+
|
|
8
|
+
- `npm` or `yarn`
|
|
20
9
|
- Microphone access via browser permissions
|
|
21
10
|
- Stable network connectivity
|
|
11
|
+
- Access token from Eka Care
|
|
22
12
|
|
|
23
13
|
## Installation
|
|
24
14
|
|
|
25
|
-
Install the SDK using `npm` or `yarn`:
|
|
26
|
-
|
|
27
15
|
```bash
|
|
28
16
|
npm install @eka-care/ekascribe-ts-sdk
|
|
29
17
|
# or
|
|
@@ -32,20 +20,19 @@ yarn add @eka-care/ekascribe-ts-sdk
|
|
|
32
20
|
|
|
33
21
|
## Bundler Setup
|
|
34
22
|
|
|
35
|
-
The SDK uses a SharedWorker for background audio uploads. Modern bundlers
|
|
23
|
+
The SDK uses a SharedWorker for background audio uploads. Modern bundlers handle this automatically.
|
|
36
24
|
|
|
37
25
|
### Vite
|
|
38
26
|
|
|
39
|
-
Works out of the box
|
|
27
|
+
Works out of the box.
|
|
40
28
|
|
|
41
29
|
```ts
|
|
42
|
-
// Just import and use
|
|
43
30
|
import { getEkaScribeInstance } from '@eka-care/ekascribe-ts-sdk';
|
|
44
31
|
```
|
|
45
32
|
|
|
46
33
|
### Webpack 5
|
|
47
34
|
|
|
48
|
-
Works out of the box
|
|
35
|
+
Works out of the box. The `new URL(..., import.meta.url)` pattern is natively supported.
|
|
49
36
|
|
|
50
37
|
```ts
|
|
51
38
|
import { getEkaScribeInstance } from '@eka-care/ekascribe-ts-sdk';
|
|
@@ -53,1591 +40,1147 @@ import { getEkaScribeInstance } from '@eka-care/ekascribe-ts-sdk';
|
|
|
53
40
|
|
|
54
41
|
### Next.js
|
|
55
42
|
|
|
56
|
-
|
|
43
|
+
Ensure the SDK is only used on the client side:
|
|
57
44
|
|
|
58
45
|
```tsx
|
|
59
46
|
'use client';
|
|
60
47
|
|
|
61
48
|
import { getEkaScribeInstance } from '@eka-care/ekascribe-ts-sdk';
|
|
62
|
-
|
|
63
|
-
// Use inside a client component
|
|
64
|
-
const ekascribe = getEkaScribeInstance({ access_token: 'your_token' });
|
|
65
49
|
```
|
|
66
50
|
|
|
67
51
|
### Browser (Script Tag)
|
|
68
52
|
|
|
69
|
-
For direct browser usage without a bundler:
|
|
70
|
-
|
|
71
53
|
```html
|
|
72
54
|
<script type="module">
|
|
73
55
|
import { getEkaScribeInstance } from 'https://cdn.jsdelivr.net/npm/@eka-care/ekascribe-ts-sdk/dist/index.mjs';
|
|
74
|
-
|
|
75
|
-
const ekascribe = getEkaScribeInstance({ access_token: 'your_token' });
|
|
76
56
|
</script>
|
|
77
57
|
```
|
|
78
58
|
|
|
79
|
-
|
|
59
|
+
---
|
|
80
60
|
|
|
81
|
-
|
|
61
|
+
## Instance Management
|
|
82
62
|
|
|
83
|
-
|
|
63
|
+
The SDK uses a **singleton pattern**. `getEkaScribeInstance()` always returns the same instance for a given `env` + `clientId` combination.
|
|
84
64
|
|
|
85
65
|
```ts
|
|
86
|
-
|
|
87
|
-
const sdkConfig = {
|
|
88
|
-
access_token: '<your_access_token>',
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// Get instance and use it throughout your application
|
|
92
|
-
const ekascribe = getEkaScribeInstance(sdkConfig);
|
|
66
|
+
const ekascribe = getEkaScribeInstance(config);
|
|
93
67
|
```
|
|
94
68
|
|
|
95
|
-
|
|
69
|
+
- Calling `getEkaScribeInstance()` multiple times with the same config returns the same instance.
|
|
70
|
+
- If `env` or `clientId` changes, the old instance is automatically reset.
|
|
71
|
+
- If only `access_token` changes, the token is updated on the existing instance without resetting.
|
|
72
|
+
- The SDK supports **one active recording at a time**. Always call `endRecording()` or `cancelSession()` before starting a new recording. If you call `startRecordingV2()` while a recording is active, the SDK cleans up the old recording locally but does **not** end the session on the server — the old session will be left abandoned until it expires.
|
|
96
73
|
|
|
97
|
-
|
|
74
|
+
---
|
|
98
75
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
const config = await ekascribe.getEkascribeConfig();
|
|
103
|
-
```
|
|
76
|
+
## Integration Guide
|
|
104
77
|
|
|
105
|
-
|
|
78
|
+
### Step 1: Initialize the SDK
|
|
106
79
|
|
|
107
80
|
```ts
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
"supported_languages": [
|
|
111
|
-
{ "id": "en", "name": "English" },
|
|
112
|
-
{ "id": "hi", "name": "Hindi" }
|
|
113
|
-
],
|
|
114
|
-
"supported_output_formats": [{ "id": "clinical-notes-template", "name": "Clinical Notes" }],
|
|
115
|
-
"consultation_modes": [
|
|
116
|
-
{
|
|
117
|
-
"id": "consultation",
|
|
118
|
-
"name": "Consultation",
|
|
119
|
-
"desc": "Eka Scribe will listen to your conversation and create clinical notes"
|
|
120
|
-
}
|
|
121
|
-
],
|
|
122
|
-
"max_selection": {
|
|
123
|
-
"supported_languages": 2,
|
|
124
|
-
"supported_output_formats": 2,
|
|
125
|
-
"consultation_modes": 1
|
|
126
|
-
},
|
|
127
|
-
"user_details": {
|
|
128
|
-
"fn": "Dr. John",
|
|
129
|
-
"mn": "",
|
|
130
|
-
"ln": "Doe",
|
|
131
|
-
"dob": "1985-06-15",
|
|
132
|
-
"gen": "M",
|
|
133
|
-
"s": "active",
|
|
134
|
-
"is_paid_doc": true,
|
|
135
|
-
"uuid": "user-uuid-123"
|
|
136
|
-
},
|
|
137
|
-
"wid": "workspace-id-456"
|
|
138
|
-
},
|
|
139
|
-
"message": "Configuration fetched successfully",
|
|
140
|
-
"code": 200
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### 3. Fetch user's favorite templates
|
|
145
|
-
|
|
146
|
-
Get the list of templates marked as favorites by the user (configured via `my_templates` in the config).
|
|
147
|
-
|
|
148
|
-
```ts
|
|
149
|
-
const myTemplates = await ekascribe.getConfigMyTemplates();
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
- #### Sample Response:
|
|
81
|
+
import { getEkaScribeInstance } from '@eka-care/ekascribe-ts-sdk';
|
|
82
|
+
import type { EkaScribeConfig } from '@eka-care/ekascribe-ts-sdk';
|
|
153
83
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
{
|
|
163
|
-
"id": "template_456",
|
|
164
|
-
"name": "Cardiology Template"
|
|
165
|
-
}
|
|
166
|
-
],
|
|
84
|
+
const config: EkaScribeConfig = {
|
|
85
|
+
access_token: '<your_access_token>',
|
|
86
|
+
env: 'PROD', // 'PROD' | 'DEV'
|
|
87
|
+
clientId: '<your_client_id>', // optional
|
|
88
|
+
allianceConfig: {
|
|
89
|
+
baseUrl: 'https://api.eka.care/voice/v1', // required
|
|
90
|
+
useWorker: 'auto', // optional: true | false | 'auto'
|
|
91
|
+
debug: false, // optional
|
|
167
92
|
},
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
**Note:** The `my_templates` field contains templates that were previously saved using the `updateConfig()` method (see Templates SDK Methods section).
|
|
174
|
-
|
|
175
|
-
### 4. Init transaction
|
|
176
|
-
|
|
177
|
-
Initialize a transaction before starting recording. This sets up the session with your configuration.
|
|
93
|
+
sharedWorkerUrl: workerUrl, // optional — see SharedWorker section
|
|
94
|
+
};
|
|
178
95
|
|
|
179
|
-
|
|
180
|
-
const response = await ekascribe.initTransaction({
|
|
181
|
-
mode: 'consultation',
|
|
182
|
-
input_language: ['en-IN'],
|
|
183
|
-
output_format_template: [{
|
|
184
|
-
template_id: 'your_template_id',
|
|
185
|
-
codification_needed?: true // optional
|
|
186
|
-
}],
|
|
187
|
-
txn_id: 'unique-transaction-id',
|
|
188
|
-
transfer: 'vaded' | 'non-vaded',
|
|
189
|
-
model_type: 'pro' | 'lite',
|
|
190
|
-
system_info: {
|
|
191
|
-
platform: 'web',
|
|
192
|
-
language: 'en',
|
|
193
|
-
time_zone: 'Asia/Kolkata',
|
|
194
|
-
},
|
|
195
|
-
patient_details: {
|
|
196
|
-
username: 'John Doe',
|
|
197
|
-
age: 35,
|
|
198
|
-
biologicalSex: 'M',
|
|
199
|
-
},
|
|
200
|
-
version: '1.0.0',
|
|
201
|
-
additional_data: {},
|
|
202
|
-
},
|
|
203
|
-
sharedWorkerUrl: 'worker-url', // optional - see Shared Worker Configuration below
|
|
204
|
-
);
|
|
96
|
+
const ekascribe = getEkaScribeInstance(config);
|
|
205
97
|
```
|
|
206
98
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
- `input_language`: Language code array (e.g., `['en-IN']`)
|
|
210
|
-
- `output_format_template`: Array with `template_id` - depends on your end user's template selection
|
|
211
|
-
- `system_info`: Optional - Pass your system configuration to backend
|
|
212
|
-
- `patient_details`: Optional - Patient information
|
|
213
|
-
- `version`: SDK version
|
|
214
|
-
- `additional_data`: Optional - Pass any data you want to receive unchanged in the response
|
|
215
|
-
- `transfer`: Audio mode. Use `vaded` for audio already processed with Voice Activity Detection (SDK does this by default); use `non-vaded` only if you are sending raw audio without VAD.
|
|
216
|
-
- `model_type`: Transcription model choice. `pro` = most accurate; `lite` = lower latency, more performant.
|
|
217
|
-
- `sharedWorkerUrl`: Optional - Custom URL for the shared worker script. See **Shared Worker Configuration** section below for usage details.
|
|
218
|
-
|
|
219
|
-
- #### Sample Response:
|
|
99
|
+
#### `EkaScribeConfig`
|
|
220
100
|
|
|
221
101
|
```ts
|
|
222
|
-
{
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
102
|
+
interface EkaScribeConfig {
|
|
103
|
+
access_token?: string; // Bearer token for authentication
|
|
104
|
+
env: 'PROD' | 'DEV'; // Environment
|
|
105
|
+
clientId?: string; // Your client identifier
|
|
106
|
+
mode?: 'http' | 'ipc'; // Transport mode (default: 'http')
|
|
107
|
+
ipcBridge?: IpcBridge; // Required when mode is 'ipc' (Electron apps)
|
|
108
|
+
enableTracking?: boolean; // Enable internal analytics tracking
|
|
109
|
+
flavour?: string; // Client flavour identifier
|
|
110
|
+
sharedWorkerUrl?: string; // URL to worker.bundle.js for background uploads
|
|
111
|
+
allianceConfig?: {
|
|
112
|
+
baseUrl?: string; // Scribe service URL (required)
|
|
113
|
+
useWorker?: boolean | 'auto'; // SharedWorker: true | false | 'auto' (default: 'auto')
|
|
114
|
+
debug?: boolean; // Enable debug logging (default: false)
|
|
115
|
+
};
|
|
116
|
+
widget?: WidgetConfig; // Widget configuration — see Widget section
|
|
230
117
|
}
|
|
231
118
|
```
|
|
232
119
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
Possible Error Codes in `error_code`:
|
|
236
|
-
|
|
237
|
-
- `txn_limit_exceeded`: Maximum number of transactions exceeded
|
|
238
|
-
- `txn_init_failed`: Something went wrong. Retry with the same method call
|
|
239
|
-
|
|
240
|
-
**Handling 401 Status Code:**
|
|
120
|
+
### Step 2: Register Callbacks
|
|
241
121
|
|
|
242
|
-
|
|
122
|
+
Register callbacks **before** starting a recording. Events fire immediately once recording starts.
|
|
243
123
|
|
|
244
124
|
```ts
|
|
245
|
-
|
|
246
|
-
sdkConfig.access_token = '<new_access_token>';
|
|
125
|
+
import type { EkaCallbackMap } from '@eka-care/ekascribe-ts-sdk';
|
|
247
126
|
|
|
248
|
-
//
|
|
249
|
-
ekascribe.
|
|
127
|
+
// Token refresh — SDK calls this automatically on 401
|
|
128
|
+
ekascribe.registerCallback('onTokenRequired', async () => {
|
|
129
|
+
const newToken = await fetchFreshToken(); // your token refresh logic
|
|
130
|
+
return newToken; // must return the new token string
|
|
131
|
+
});
|
|
250
132
|
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
133
|
+
// Recording state changes
|
|
134
|
+
ekascribe.registerCallback('onRecordingStateChange', (event) => {
|
|
135
|
+
console.log('State:', event.type); // 'started' | 'paused' | 'resumed' | 'ended'
|
|
136
|
+
});
|
|
254
137
|
|
|
255
|
-
|
|
138
|
+
// Upload progress
|
|
139
|
+
ekascribe.registerCallback('onUploadEvent', (event) => {
|
|
140
|
+
if (event.type === 'progress') {
|
|
141
|
+
console.log(`Uploaded ${event.data.successCount}/${event.data.totalCount}`);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
256
144
|
|
|
257
|
-
|
|
145
|
+
// Errors
|
|
146
|
+
ekascribe.registerCallback('onError', (event) => {
|
|
147
|
+
console.error(`[${event.error.code}] ${event.error.message}`);
|
|
148
|
+
});
|
|
149
|
+
```
|
|
258
150
|
|
|
259
|
-
|
|
151
|
+
See [Callbacks Reference](#callbacks-reference) for all callback types and payloads.
|
|
260
152
|
|
|
261
|
-
|
|
262
|
-
- **Resource Efficiency**: A single shared worker instance can handle uploads across multiple tabs/windows
|
|
263
|
-
- **Improved User Experience**: Prevents UI blocking during large file uploads
|
|
153
|
+
### Step 3: Start Recording
|
|
264
154
|
|
|
265
|
-
|
|
155
|
+
Creates a session and starts the microphone in one call.
|
|
266
156
|
|
|
267
157
|
```ts
|
|
268
|
-
|
|
269
|
-
async function getSharedWorkerUrl() {
|
|
270
|
-
// Fetch the worker script from CDN (or your own hosting) - update the latest sdk version
|
|
271
|
-
const workerScriptResponse = await fetch(
|
|
272
|
-
'https://cdn.jsdelivr.net/npm/@eka-care/ekascribe-ts-sdk@2.0.48/dist/worker.bundle.js'
|
|
273
|
-
);
|
|
274
|
-
const workerScript = await workerScriptResponse.text();
|
|
158
|
+
import type { RecordingOptions } from '@eka-care/ekascribe-ts-sdk';
|
|
275
159
|
|
|
276
|
-
|
|
277
|
-
|
|
160
|
+
const options: RecordingOptions = {
|
|
161
|
+
templates: ['template-id'], // required: template IDs for output
|
|
162
|
+
sessionMode: 'consultation', // optional: 'consultation' | 'dictation'
|
|
163
|
+
languageHint: ['en', 'hi'], // optional: input audio language hints
|
|
164
|
+
transcriptLanguage: 'en', // optional: output transcript language
|
|
165
|
+
model: 'pro', // optional: 'pro' | 'lite'
|
|
166
|
+
uploadType: 'chunked', // optional: 'chunked' (default) | 'single'
|
|
167
|
+
deviceId: microphoneId, // optional: specific microphone device ID
|
|
168
|
+
patientDetails: { // optional
|
|
169
|
+
name: 'John Doe',
|
|
170
|
+
age: '45',
|
|
171
|
+
gender: 'male',
|
|
172
|
+
},
|
|
173
|
+
additionalData: {}, // optional: any extra data for the session
|
|
174
|
+
};
|
|
278
175
|
|
|
279
|
-
|
|
280
|
-
const workerUrl = URL.createObjectURL(blob);
|
|
176
|
+
const result = await ekascribe.startRecordingV2(options);
|
|
281
177
|
|
|
282
|
-
|
|
178
|
+
if (result.error_code) {
|
|
179
|
+
console.error(result.error_code, result.message);
|
|
180
|
+
return;
|
|
283
181
|
}
|
|
284
182
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const response = await ekascribe.initTransaction(
|
|
289
|
-
{
|
|
290
|
-
mode: 'consultation',
|
|
291
|
-
input_language: ['en-IN'],
|
|
292
|
-
output_format_template: [{ template_id: 'your_template_id' }],
|
|
293
|
-
txn_id: 'unique-transaction-id',
|
|
294
|
-
transfer: 'vaded',
|
|
295
|
-
model_type: 'pro',
|
|
296
|
-
// ... other parameters
|
|
297
|
-
},
|
|
298
|
-
sharedWorkerUrl // Pass the custom worker URL;
|
|
299
|
-
);
|
|
183
|
+
const sessionId = result.txn_id;
|
|
184
|
+
console.log('Recording started:', sessionId);
|
|
300
185
|
```
|
|
301
186
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
- Make sure to use the correct SDK version in the CDN URL
|
|
305
|
-
- The worker URL must be accessible from the same origin or have proper CORS headers
|
|
306
|
-
- Remember to revoke the object URL when you're done: `URL.revokeObjectURL(workerUrl)`
|
|
307
|
-
- If `sharedWorkerUrl` is not provided, the SDK will use the main thread for file uploads (no shared worker)
|
|
308
|
-
|
|
309
|
-
### 5. Start recording
|
|
310
|
-
|
|
311
|
-
Start recording audio after initializing the transaction.
|
|
187
|
+
#### `RecordingOptions`
|
|
312
188
|
|
|
313
189
|
```ts
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
// - "vad_not_initialized" -> VAD failed to initialize; reinitialize and retry the same function call
|
|
327
|
-
error_code?: ERROR_CODE
|
|
190
|
+
interface RecordingOptions {
|
|
191
|
+
templates: string[]; // Template IDs for extraction (required)
|
|
192
|
+
model?: string; // Model ID ('pro' | 'lite')
|
|
193
|
+
languageHint?: string[]; // Language codes for audio input
|
|
194
|
+
transcriptLanguage?: string; // Language code for transcript output
|
|
195
|
+
uploadType?: string; // 'chunked' | 'single' (default: 'chunked')
|
|
196
|
+
communicationProtocol?: string; // 'http' (default)
|
|
197
|
+
additionalData?: Record<string, any>; // Extra data for the session
|
|
198
|
+
deviceId?: string; // Specific microphone device ID
|
|
199
|
+
sessionMode?: string; // 'consultation' | 'dictation'
|
|
200
|
+
patientDetails?: PatientDetails; // Patient info
|
|
201
|
+
sessionId?: string; // External session/transaction ID
|
|
328
202
|
}
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
### 6. Pause recording
|
|
332
203
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
204
|
+
interface PatientDetails {
|
|
205
|
+
oid?: string;
|
|
206
|
+
name?: string;
|
|
207
|
+
age?: string;
|
|
208
|
+
gender?: string;
|
|
209
|
+
mobile?: number;
|
|
210
|
+
}
|
|
337
211
|
```
|
|
338
212
|
|
|
339
|
-
|
|
213
|
+
#### Response: `TStartRecordingResponse`
|
|
340
214
|
|
|
341
215
|
```ts
|
|
342
|
-
{
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
216
|
+
type TStartRecordingResponse = {
|
|
217
|
+
error_code?: ERROR_CODE; // present only on error
|
|
218
|
+
status_code: number;
|
|
219
|
+
message: string;
|
|
220
|
+
txn_id?: string; // session ID — present on success
|
|
221
|
+
business_id?: string;
|
|
222
|
+
oid?: string;
|
|
223
|
+
uuid?: string;
|
|
224
|
+
};
|
|
348
225
|
```
|
|
349
226
|
|
|
350
|
-
###
|
|
351
|
-
|
|
352
|
-
Resume a paused recording.
|
|
227
|
+
### Step 4: Pause / Resume
|
|
353
228
|
|
|
354
229
|
```ts
|
|
355
|
-
const
|
|
230
|
+
const pauseResult = ekascribe.pauseRecording();
|
|
231
|
+
// later...
|
|
232
|
+
const resumeResult = ekascribe.resumeRecording();
|
|
356
233
|
```
|
|
357
234
|
|
|
358
|
-
|
|
235
|
+
#### Response: `TPauseRecordingResponse`
|
|
359
236
|
|
|
360
237
|
```ts
|
|
361
|
-
{
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
238
|
+
type TPauseRecordingResponse = {
|
|
239
|
+
status_code: number;
|
|
240
|
+
message: string;
|
|
241
|
+
error_code?: ERROR_CODE;
|
|
242
|
+
is_paused?: boolean;
|
|
243
|
+
};
|
|
367
244
|
```
|
|
368
245
|
|
|
369
|
-
###
|
|
370
|
-
|
|
371
|
-
End the recording session. This method:
|
|
246
|
+
### Step 5: End Recording
|
|
372
247
|
|
|
373
|
-
|
|
374
|
-
- Uploads all audio chunks to the server
|
|
375
|
-
- Automatically retries failed uploads once
|
|
376
|
-
- Calls the commit API to finalize the transaction
|
|
248
|
+
Stops the microphone, flushes pending audio, waits for all uploads, and ends the session on the server (triggers processing).
|
|
377
249
|
|
|
378
250
|
```ts
|
|
379
|
-
const
|
|
380
|
-
```
|
|
251
|
+
const endResult = await ekascribe.endRecording();
|
|
381
252
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
253
|
+
if (endResult.error_code) {
|
|
254
|
+
switch (endResult.error_code) {
|
|
255
|
+
case 'audio_upload_failed':
|
|
256
|
+
// Some audio files failed to upload — retry
|
|
257
|
+
await ekascribe.retryUploadRecording();
|
|
258
|
+
break;
|
|
259
|
+
case 'end_recording_failed':
|
|
260
|
+
case 'internal_server_error':
|
|
261
|
+
console.error(endResult.error_code, endResult.message);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
391
264
|
}
|
|
392
265
|
```
|
|
393
266
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
Possible Error Codes in `error_code`:
|
|
397
|
-
|
|
398
|
-
- `txn_stop_failed`: Call `endRecording` again.
|
|
399
|
-
- `audio_upload_failed`: Use `retryUploadRecording` (step 9).
|
|
400
|
-
- `txn_commit_failed`: Call `commitTransactionCall` (step 11).
|
|
401
|
-
|
|
402
|
-
**Handling 401 Status Code:**
|
|
403
|
-
|
|
404
|
-
If you receive a `status_code: 401`, update the tokens in your config and retry:
|
|
267
|
+
#### Response: `TEndRecordingResponse`
|
|
405
268
|
|
|
406
269
|
```ts
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const response = await ekascribe.endRecording();
|
|
270
|
+
type TEndRecordingResponse = {
|
|
271
|
+
error_code?: ERROR_CODE;
|
|
272
|
+
status_code: number;
|
|
273
|
+
message: string;
|
|
274
|
+
failed_files?: string[]; // files that failed to upload
|
|
275
|
+
total_audio_files?: string[]; // all audio files generated
|
|
276
|
+
};
|
|
415
277
|
```
|
|
416
278
|
|
|
417
|
-
###
|
|
279
|
+
### Step 6: Get Output
|
|
418
280
|
|
|
419
|
-
|
|
281
|
+
Use `getSessionStatus()` to poll for results after ending the recording.
|
|
420
282
|
|
|
421
283
|
```ts
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
284
|
+
const status = await ekascribe.getSessionStatus(sessionId, {
|
|
285
|
+
poll: {
|
|
286
|
+
maxAttempts: 60,
|
|
287
|
+
intervalMs: 2000,
|
|
288
|
+
signal: abortController.signal, // optional: cancel polling early
|
|
289
|
+
onProgress: (sessionData) => {
|
|
290
|
+
console.log('Status:', sessionData.status);
|
|
291
|
+
// Display partial results as they come in
|
|
292
|
+
if (sessionData.templates) {
|
|
293
|
+
console.log('Templates:', sessionData.templates);
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
},
|
|
427
297
|
});
|
|
428
|
-
```
|
|
429
298
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
Status codes to handle:
|
|
436
|
-
|
|
437
|
-
- `200`: Success; all templates processed.
|
|
438
|
-
- `202`: Templates are still processing; poll again (or let `pollSessionOutput` continue).
|
|
439
|
-
- `206`: Partial success; some templates not processed fully.
|
|
440
|
-
- `401`: Authentication token expired. Update the token.
|
|
441
|
-
- `403`: Invalid Authentication token. Pass new token.
|
|
442
|
-
- `500`: All template processing failed, or internal server error; stop and surface error.
|
|
443
|
-
|
|
444
|
-
- #### Response type:
|
|
445
|
-
|
|
446
|
-
```ts
|
|
447
|
-
{
|
|
448
|
-
response?: {
|
|
449
|
-
data: {
|
|
450
|
-
output: TOutputSummary[];
|
|
451
|
-
template_results: {
|
|
452
|
-
integration: TOutputSummary[];
|
|
453
|
-
custom: TOutputSummary[];
|
|
454
|
-
transcript: TOutputSummary[];
|
|
455
|
-
};
|
|
456
|
-
audio_matrix?: {
|
|
457
|
-
quality: string;
|
|
458
|
-
};
|
|
459
|
-
additional_data?: {};
|
|
460
|
-
created_at?: string;
|
|
461
|
-
};
|
|
462
|
-
error?: {
|
|
463
|
-
code: string;
|
|
464
|
-
msg: string;
|
|
465
|
-
};
|
|
466
|
-
} | null;
|
|
467
|
-
status_code: number;
|
|
468
|
-
message?: string;
|
|
299
|
+
if (status.success) {
|
|
300
|
+
console.log('Final status:', status.data.status);
|
|
301
|
+
console.log('Templates:', status.data.templates);
|
|
302
|
+
console.log('Transcript:', status.data.transcript);
|
|
469
303
|
}
|
|
304
|
+
```
|
|
470
305
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
306
|
+
#### Response: `SDKResult<GetSessionStatusResponse>`
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
// All async Alliance methods return SDKResult — check result.success
|
|
310
|
+
type SDKResult<T> =
|
|
311
|
+
| { success: true; data: T; httpStatus?: number }
|
|
312
|
+
| { success: false; error: ScribeError };
|
|
313
|
+
|
|
314
|
+
type GetSessionStatusResponse = {
|
|
315
|
+
session_id: string;
|
|
316
|
+
status: SessionStatus;
|
|
317
|
+
created_at: string;
|
|
318
|
+
expires_at?: string | null;
|
|
319
|
+
completed_at?: string | null;
|
|
320
|
+
model_used?: string | null;
|
|
321
|
+
language_detected?: string | null;
|
|
322
|
+
audio_files_received: number;
|
|
323
|
+
audio_files: string[];
|
|
324
|
+
audio_files_processed?: number;
|
|
325
|
+
additional_data: Record<string, any>;
|
|
326
|
+
templates?: TemplateEntry[];
|
|
327
|
+
transcript?: string;
|
|
328
|
+
processing_errors?: ProcessingError[];
|
|
329
|
+
error?: { code: string; message: string };
|
|
487
330
|
};
|
|
488
331
|
```
|
|
489
332
|
|
|
490
|
-
|
|
333
|
+
### Step 7: Clean Up
|
|
491
334
|
|
|
492
335
|
```ts
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
response: {
|
|
496
|
-
data: {
|
|
497
|
-
output: [
|
|
498
|
-
{
|
|
499
|
-
template_id: "template_id_passed_in_initTransaction",
|
|
500
|
-
value: "Output Data for this template",
|
|
501
|
-
type: "custom",
|
|
502
|
-
name: "General Prescription",
|
|
503
|
-
status: "success"
|
|
504
|
-
}
|
|
505
|
-
],
|
|
506
|
-
template_results: {
|
|
507
|
-
custom: [
|
|
508
|
-
{
|
|
509
|
-
template_id: "custom_template",
|
|
510
|
-
value: "Output prescription",
|
|
511
|
-
type: "custom",
|
|
512
|
-
name: "Custom Medication Template",
|
|
513
|
-
status: "partial_success",
|
|
514
|
-
warnings: [
|
|
515
|
-
{
|
|
516
|
-
type: "warning",
|
|
517
|
-
code: "FIELD_MISSING",
|
|
518
|
-
msg: "Dosage information not found"
|
|
519
|
-
}
|
|
520
|
-
]
|
|
521
|
-
}
|
|
522
|
-
]
|
|
523
|
-
},
|
|
524
|
-
audio_matrix: {
|
|
525
|
-
quality: "4.5"
|
|
526
|
-
},
|
|
527
|
-
created_at: "2024-11-19T10:30:00Z"
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
336
|
+
// Reset the singleton — clears all state, destroys widget, removes callbacks
|
|
337
|
+
await ekascribe.resetInstance();
|
|
531
338
|
```
|
|
532
339
|
|
|
533
|
-
|
|
340
|
+
After calling `resetInstance()`, you must call `getEkaScribeInstance()` again to get a new instance.
|
|
534
341
|
|
|
535
|
-
|
|
342
|
+
### Flow Diagram
|
|
536
343
|
|
|
537
|
-
```ts
|
|
538
|
-
const response = await ekascribe.retryUploadRecording({ force_commit: true });
|
|
539
344
|
```
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
345
|
+
getEkaScribeInstance(config)
|
|
346
|
+
|
|
|
347
|
+
v
|
|
348
|
+
registerCallback() ── Set up event handlers before recording
|
|
349
|
+
|
|
|
350
|
+
v
|
|
351
|
+
startRecordingV2() ── Creates session + starts mic + begins upload
|
|
352
|
+
|
|
|
353
|
+
pause / resume ── Optional during recording
|
|
354
|
+
|
|
|
355
|
+
v
|
|
356
|
+
endRecording() ── Stops mic + flushes audio + ends session
|
|
357
|
+
|
|
|
358
|
+
v
|
|
359
|
+
getSessionStatus() ── Poll until completed/failed
|
|
360
|
+
|
|
|
361
|
+
v
|
|
362
|
+
Read results ── templates, transcript, errors
|
|
551
363
|
```
|
|
552
364
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
- `force_commit: true` - Model will initiate the processing if some files still fail after retry
|
|
556
|
-
- `force_commit: false` - It will waits until all files are uploaded successfully before processing.
|
|
365
|
+
---
|
|
557
366
|
|
|
558
|
-
|
|
367
|
+
## Callbacks Reference
|
|
559
368
|
|
|
560
|
-
|
|
369
|
+
Register with `registerCallback(name, handler)`. Remove with `removeCallback(name, handler)`.
|
|
561
370
|
|
|
562
|
-
|
|
563
|
-
// Update tokens in your config variable
|
|
564
|
-
sdkConfig.access_token = '<new_access_token>';
|
|
371
|
+
### `onTokenRequired`
|
|
565
372
|
|
|
566
|
-
|
|
567
|
-
ekascribe.updateAuthTokens({ access_token: sdkConfig.access_token });
|
|
568
|
-
|
|
569
|
-
// Now retry the method call
|
|
570
|
-
const response = await ekascribe.retryUploadRecording({ force_commit: true });
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
### 11. Patch recording session status
|
|
574
|
-
|
|
575
|
-
Cancel or update the status of a recording session.
|
|
373
|
+
Called automatically when the SDK receives a 401 from any API call. Your handler must return a fresh access token. The SDK will update its internal token and retry the failed request.
|
|
576
374
|
|
|
577
375
|
```ts
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
error: {
|
|
583
|
-
// Pass these exact values without changing them
|
|
584
|
-
type: 'user_action',
|
|
585
|
-
code: 'cancelled_by_user',
|
|
586
|
-
msg: 'Session cancelled by user',
|
|
587
|
-
},
|
|
588
|
-
},
|
|
376
|
+
ekascribe.registerCallback('onTokenRequired', async () => {
|
|
377
|
+
// Call your auth service to get a fresh token
|
|
378
|
+
const newToken = await myAuthService.refreshToken();
|
|
379
|
+
return newToken; // return the token string
|
|
589
380
|
});
|
|
590
381
|
```
|
|
591
382
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
"message": "Session status updated successfully",
|
|
598
|
-
"code": 200,
|
|
599
|
-
error?: {
|
|
600
|
-
code: string;
|
|
601
|
-
message: string;
|
|
602
|
-
display_message: string;
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
**Handling 401 Status Code:**
|
|
383
|
+
**Important:**
|
|
384
|
+
- The handler must return `Promise<string>` or `string`
|
|
385
|
+
- The SDK times out after 10 seconds — if your refresh takes longer, the request fails
|
|
386
|
+
- Once the token is returned, the SDK calls `updateAuthTokens()` internally — you don't need to call it yourself
|
|
387
|
+
- This replaces the old pattern of manually checking `status_code: 401` in every response
|
|
608
388
|
|
|
609
|
-
|
|
389
|
+
#### Type
|
|
610
390
|
|
|
611
391
|
```ts
|
|
612
|
-
//
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
// Update tokens in the instance
|
|
616
|
-
ekascribe.updateAuthTokens({ access_token: sdkConfig.access_token });
|
|
617
|
-
|
|
618
|
-
// Now retry the method call
|
|
619
|
-
const response = await ekascribe.patchSessionStatus({ ... });
|
|
392
|
+
// Consumer-facing type (EkaScribe SDK)
|
|
393
|
+
onTokenRequired: () => Promise<string> | string;
|
|
620
394
|
```
|
|
621
395
|
|
|
622
|
-
###
|
|
396
|
+
### `onRecordingStateChange`
|
|
623
397
|
|
|
624
|
-
|
|
398
|
+
Fired when recording state transitions.
|
|
625
399
|
|
|
626
400
|
```ts
|
|
627
|
-
|
|
401
|
+
ekascribe.registerCallback('onRecordingStateChange', (event) => {
|
|
402
|
+
console.log(event.type); // 'started' | 'paused' | 'resumed' | 'ended'
|
|
403
|
+
});
|
|
628
404
|
```
|
|
629
405
|
|
|
630
|
-
|
|
406
|
+
#### Type
|
|
631
407
|
|
|
632
408
|
```ts
|
|
633
|
-
{
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
409
|
+
interface RecordingStateChangeEvent {
|
|
410
|
+
type: 'started' | 'paused' | 'resumed' | 'ended';
|
|
411
|
+
timestamp: string;
|
|
412
|
+
data?: any;
|
|
413
|
+
}
|
|
638
414
|
```
|
|
639
415
|
|
|
640
|
-
|
|
416
|
+
### `onAudioEvent`
|
|
641
417
|
|
|
642
|
-
|
|
418
|
+
Fired for speech detection, silence warnings, chunk creation, and frame processing.
|
|
643
419
|
|
|
644
420
|
```ts
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
421
|
+
ekascribe.registerCallback('onAudioEvent', (event) => {
|
|
422
|
+
switch (event.type) {
|
|
423
|
+
case 'user_speech':
|
|
424
|
+
console.log('Speaking:', event.data.isSpeaking);
|
|
425
|
+
break;
|
|
426
|
+
case 'silence_warning':
|
|
427
|
+
console.log('Silence duration:', event.data.durationMs, 'ms');
|
|
428
|
+
break;
|
|
429
|
+
case 'chunk_ready':
|
|
430
|
+
console.log('Chunk:', event.data.fileName);
|
|
431
|
+
break;
|
|
432
|
+
case 'frame_processed':
|
|
433
|
+
// Raw audio frame data
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
});
|
|
653
437
|
```
|
|
654
438
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
Use this method to stop a transaction that has not yet been stopped or returned a `txn_stop_failed` error in a previous step.
|
|
439
|
+
#### Type
|
|
658
440
|
|
|
659
441
|
```ts
|
|
660
|
-
|
|
442
|
+
type AudioEvent =
|
|
443
|
+
| { type: 'user_speech'; timestamp: string; data: { isSpeaking: boolean } }
|
|
444
|
+
| { type: 'silence_warning'; timestamp: string; data: { durationMs: number } }
|
|
445
|
+
| { type: 'chunk_ready'; timestamp: string; data: { chunkIndex: number; fileName: string; chunkData: Uint8Array[] } }
|
|
446
|
+
| { type: 'frame_processed'; timestamp: string; data: { isSpeech: number; notSpeech: number; frame: Float32Array; duration: number } };
|
|
661
447
|
```
|
|
662
448
|
|
|
663
|
-
|
|
449
|
+
### `onUploadEvent`
|
|
450
|
+
|
|
451
|
+
Fired for upload progress, failures, and retries.
|
|
664
452
|
|
|
665
453
|
```ts
|
|
666
|
-
{
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
454
|
+
ekascribe.registerCallback('onUploadEvent', (event) => {
|
|
455
|
+
switch (event.type) {
|
|
456
|
+
case 'progress':
|
|
457
|
+
console.log(`${event.data.successCount}/${event.data.totalCount} uploaded`);
|
|
458
|
+
break;
|
|
459
|
+
case 'failed':
|
|
460
|
+
console.error(`Upload failed: ${event.data.fileName}`, event.data.error);
|
|
461
|
+
break;
|
|
462
|
+
case 'retry':
|
|
463
|
+
console.log(`Retrying ${event.data.fileName}, attempt ${event.data.attempt}`);
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
});
|
|
671
467
|
```
|
|
672
468
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
If you receive a `status_code: 401`, update the tokens in your config and retry:
|
|
469
|
+
#### Type
|
|
676
470
|
|
|
677
471
|
```ts
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
ekascribe.updateAuthTokens({ access_token: sdkConfig.access_token });
|
|
683
|
-
|
|
684
|
-
// Now retry the method call
|
|
685
|
-
const response = await ekascribe.stopTransactionCall();
|
|
472
|
+
type UploadEvent =
|
|
473
|
+
| { type: 'progress'; timestamp: string; data: { successCount: number; totalCount: number } }
|
|
474
|
+
| { type: 'failed'; timestamp: string; data: { fileName: string; error: string } }
|
|
475
|
+
| { type: 'retry'; timestamp: string; data: { fileName: string; attempt: number } };
|
|
686
476
|
```
|
|
687
477
|
|
|
688
|
-
###
|
|
478
|
+
### `onSessionEvent`
|
|
689
479
|
|
|
690
|
-
|
|
480
|
+
Fired on session lifecycle events.
|
|
691
481
|
|
|
692
482
|
```ts
|
|
693
|
-
|
|
483
|
+
ekascribe.registerCallback('onSessionEvent', (event) => {
|
|
484
|
+
switch (event.type) {
|
|
485
|
+
case 'created':
|
|
486
|
+
console.log('Session created:', event.data.session_id);
|
|
487
|
+
break;
|
|
488
|
+
case 'ended':
|
|
489
|
+
console.log('Session ended:', event.data.session_id);
|
|
490
|
+
break;
|
|
491
|
+
case 'status_update':
|
|
492
|
+
console.log('Status update:', event.data.status);
|
|
493
|
+
break;
|
|
494
|
+
case 'partial_result':
|
|
495
|
+
console.log('Partial result:', event.data);
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
});
|
|
694
499
|
```
|
|
695
500
|
|
|
696
|
-
|
|
501
|
+
#### Type
|
|
697
502
|
|
|
698
503
|
```ts
|
|
699
|
-
|
|
700
|
-
data:
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
mode: "consultation",
|
|
705
|
-
oid: "174661713843153", // logged-in doctor's org ID
|
|
706
|
-
patient_details: { // present only if sent in initTransaction
|
|
707
|
-
"age": 18,
|
|
708
|
-
"biologicalSex": "M",
|
|
709
|
-
"username": ""
|
|
710
|
-
},
|
|
711
|
-
// processing_status can be: success | system_failure | request_failure | cancelled | in-progress
|
|
712
|
-
processing_status: "in-progress",
|
|
713
|
-
txn_id: "sc-c2e9be8b-46e5-489a-9473-236ddb5b24fb",
|
|
714
|
-
// user_status can be: init | commit
|
|
715
|
-
user_status: "init",
|
|
716
|
-
uuid: "c44fd76d-8de1-4011-aa54-5ddcca140f0f" // logged-in doctor's user ID
|
|
717
|
-
}
|
|
718
|
-
],
|
|
719
|
-
status: "success",
|
|
720
|
-
code: 200,
|
|
721
|
-
message: "Sessions fetched",
|
|
722
|
-
retrieved_count: 1
|
|
723
|
-
}
|
|
504
|
+
type SessionEvent =
|
|
505
|
+
| { type: 'created'; timestamp: string; data: CreateSessionResponse }
|
|
506
|
+
| { type: 'ended'; timestamp: string; data: EndSessionResponse }
|
|
507
|
+
| { type: 'status_update'; timestamp: string; data: GetSessionStatusResponse }
|
|
508
|
+
| { type: 'partial_result'; timestamp: string; data: any };
|
|
724
509
|
```
|
|
725
510
|
|
|
726
|
-
###
|
|
511
|
+
### `onError`
|
|
727
512
|
|
|
728
|
-
|
|
513
|
+
Fired on SDK errors — VAD failures, worker errors, network issues, validation errors.
|
|
729
514
|
|
|
730
515
|
```ts
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
template_id: 'new-template-id',
|
|
516
|
+
ekascribe.registerCallback('onError', (event) => {
|
|
517
|
+
console.error(`[${event.type}] ${event.error.code}: ${event.error.message}`);
|
|
734
518
|
});
|
|
735
519
|
```
|
|
736
520
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
- `txn_id`: The transaction ID of the completed session you want to convert
|
|
740
|
-
- `template_id`: The ID of the template format you want to convert the transcription into
|
|
741
|
-
|
|
742
|
-
- #### Sample Response:
|
|
521
|
+
#### Type
|
|
743
522
|
|
|
744
523
|
```ts
|
|
745
|
-
{
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
template_id: string;
|
|
750
|
-
b_id: string;
|
|
751
|
-
code: number;
|
|
752
|
-
msg: string;
|
|
753
|
-
error?: {
|
|
524
|
+
interface ErrorEvent {
|
|
525
|
+
type: 'vad_error' | 'worker_error' | 'transport_error' | 'validation_error';
|
|
526
|
+
timestamp: string;
|
|
527
|
+
error: {
|
|
754
528
|
code: string;
|
|
755
529
|
message: string;
|
|
756
|
-
|
|
530
|
+
details?: any;
|
|
757
531
|
};
|
|
758
532
|
}
|
|
759
533
|
```
|
|
760
534
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
- When you need to apply a different template to an existing transcription
|
|
764
|
-
- To generate multiple template formats from the same recording session
|
|
765
|
-
- After completing a session, when you want to see the output in a different template structure
|
|
766
|
-
|
|
767
|
-
**Note:** After getting success response from this method, call `pollSessionOutput` (Point 9) to get the output for the new template_id.
|
|
768
|
-
|
|
769
|
-
### 16. Convert transcription to template
|
|
770
|
-
|
|
771
|
-
Use this method to convert a transcription text to a specific template format.
|
|
535
|
+
### Removing Callbacks
|
|
772
536
|
|
|
773
537
|
```ts
|
|
774
|
-
const
|
|
775
|
-
txn_id: 'transaction-id-123',
|
|
776
|
-
template_id: 'target-template-id',
|
|
777
|
-
transcript: 'custom transcript text',
|
|
778
|
-
});
|
|
779
|
-
```
|
|
780
|
-
|
|
781
|
-
**Key Parameters:**
|
|
538
|
+
const handler = (event) => { /* ... */ };
|
|
782
539
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
- #### Sample Response:
|
|
788
|
-
|
|
789
|
-
```ts
|
|
790
|
-
{
|
|
791
|
-
status: 'success' | 'failed';
|
|
792
|
-
message: string;
|
|
793
|
-
txn_id: string;
|
|
794
|
-
template_id: string;
|
|
795
|
-
b_id: string;
|
|
796
|
-
code: number;
|
|
797
|
-
msg: string;
|
|
798
|
-
error?: { code: string; message: string; display_message: string };
|
|
799
|
-
}
|
|
540
|
+
ekascribe.registerCallback('onUploadEvent', handler);
|
|
541
|
+
// later...
|
|
542
|
+
ekascribe.removeCallback('onUploadEvent', handler);
|
|
800
543
|
```
|
|
801
544
|
|
|
802
|
-
###
|
|
545
|
+
### Retry Failed Uploads
|
|
803
546
|
|
|
804
|
-
|
|
547
|
+
If `endRecording()` returns `audio_upload_failed`, retry the failed uploads:
|
|
805
548
|
|
|
806
549
|
```ts
|
|
807
|
-
const
|
|
808
|
-
txnId: 'transaction-id-123',
|
|
809
|
-
data: [
|
|
810
|
-
{
|
|
811
|
-
'template-id': 'template-123',
|
|
812
|
-
data: 'Updated template output content', // Base64 Encoded data
|
|
813
|
-
},
|
|
814
|
-
],
|
|
815
|
-
});
|
|
550
|
+
const result = await ekascribe.retryUploadRecording();
|
|
816
551
|
```
|
|
817
552
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
- `txnId`: The transaction ID
|
|
821
|
-
- `data`: Array of objects containing `template-id` and the updated `data` base64 encoded data
|
|
822
|
-
|
|
823
|
-
- #### Sample Response:
|
|
553
|
+
#### Response: `TEndRecordingResponse`
|
|
824
554
|
|
|
825
555
|
```ts
|
|
826
556
|
{
|
|
827
|
-
|
|
557
|
+
status_code: number;
|
|
828
558
|
message: string;
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
error?: { code: string; message: string; display_message: string };
|
|
559
|
+
error_code?: ERROR_CODE;
|
|
560
|
+
failed_files?: string[]; // files still failing after retry
|
|
561
|
+
total_audio_files?: string[];
|
|
833
562
|
}
|
|
834
563
|
```
|
|
835
564
|
|
|
836
|
-
###
|
|
565
|
+
### Cancel a Session
|
|
837
566
|
|
|
838
|
-
|
|
567
|
+
Cancel a session **without** triggering server-side processing.
|
|
839
568
|
|
|
840
569
|
```ts
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
sharedWorker // Optional: SharedWorker instance for testing worker compatibility
|
|
847
|
-
);
|
|
570
|
+
// Cancel current active session
|
|
571
|
+
await ekascribe.cancelSession();
|
|
572
|
+
|
|
573
|
+
// Or cancel a specific session by ID
|
|
574
|
+
await ekascribe.cancelSession('session-id');
|
|
848
575
|
```
|
|
849
576
|
|
|
850
|
-
|
|
577
|
+
#### Response: `SDKResult<PatchSessionResponse>`
|
|
851
578
|
|
|
852
579
|
```ts
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
audioContext: { status: 'pass' | 'fail'; message?: string };
|
|
857
|
-
sharedWorker: { status: 'pass' | 'fail'; message?: string };
|
|
858
|
-
// ... other compatibility tests
|
|
580
|
+
const result = await ekascribe.cancelSession();
|
|
581
|
+
if (result.success) {
|
|
582
|
+
console.log('Session cancelled');
|
|
859
583
|
}
|
|
860
584
|
```
|
|
861
585
|
|
|
862
|
-
|
|
586
|
+
### Pre-Recorded Audio Upload
|
|
863
587
|
|
|
864
|
-
-
|
|
865
|
-
- To provide user feedback about missing permissions or unsupported features
|
|
866
|
-
- During onboarding to identify potential issues
|
|
588
|
+
Upload a pre-recorded audio file instead of live recording. Use this for non-real-time flows.
|
|
867
589
|
|
|
868
|
-
|
|
590
|
+
**Flow:**
|
|
869
591
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
592
|
+
1. Create session via `ekascribe.sessions.createSession()`
|
|
593
|
+
2. Upload audio via `processPreRecordedAudio()`
|
|
594
|
+
3. End session via `ekascribe.sessions.endSession()`
|
|
873
595
|
|
|
874
596
|
```ts
|
|
875
|
-
const
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
- #### Response type:
|
|
879
|
-
|
|
880
|
-
```ts
|
|
881
|
-
{
|
|
882
|
-
items: [
|
|
883
|
-
{
|
|
884
|
-
id: "123;
|
|
885
|
-
title: "Template Name";
|
|
886
|
-
desc: "Template Description";
|
|
887
|
-
section_ids: ["section-1", "section-2"];
|
|
888
|
-
is_editable: true | false;
|
|
889
|
-
}
|
|
890
|
-
];
|
|
891
|
-
code: number;
|
|
892
|
-
error?: { code: string; message: string };
|
|
893
|
-
}
|
|
894
|
-
```
|
|
895
|
-
|
|
896
|
-
### 2. Create Template
|
|
897
|
-
|
|
898
|
-
Use this method to create a new custom template.
|
|
899
|
-
|
|
900
|
-
```ts
|
|
901
|
-
const newTemplate = await ekascribe.createTemplate({
|
|
902
|
-
title: 'My Custom Template',
|
|
903
|
-
desc: 'Description of the template',
|
|
904
|
-
section_ids: ['section1', 'section2', 'section3'],
|
|
597
|
+
const result = await ekascribe.processPreRecordedAudio({
|
|
598
|
+
uploadUrl: session.upload_url, // from createSession response
|
|
599
|
+
audioFile: audioBlob, // File or Blob
|
|
905
600
|
});
|
|
906
601
|
```
|
|
907
602
|
|
|
908
|
-
|
|
603
|
+
#### Request
|
|
909
604
|
|
|
910
605
|
```ts
|
|
911
606
|
{
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
template_id?: string;
|
|
915
|
-
message?: string;
|
|
916
|
-
error?: { code: string; message: string };
|
|
607
|
+
uploadUrl: string; // S3 upload URL from session
|
|
608
|
+
audioFile: File | Blob; // the audio file
|
|
917
609
|
}
|
|
918
610
|
```
|
|
919
611
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
Use this method to update an existing template.
|
|
923
|
-
|
|
924
|
-
```ts
|
|
925
|
-
const updatedTemplate = await ekascribe.updateTemplate({
|
|
926
|
-
template_id: 'template-123',
|
|
927
|
-
title: 'Updated Template Title',
|
|
928
|
-
desc: 'Updated description',
|
|
929
|
-
section_ids: ['section1', 'section2', 'section4'],
|
|
930
|
-
});
|
|
931
|
-
```
|
|
932
|
-
|
|
933
|
-
- #### Response type:
|
|
612
|
+
#### Response: `TStartRecordingResponse`
|
|
934
613
|
|
|
935
614
|
```ts
|
|
936
615
|
{
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
message?: string;
|
|
941
|
-
error?: { code: string; message: string };
|
|
616
|
+
status_code: number;
|
|
617
|
+
message: string;
|
|
618
|
+
error_code?: ERROR_CODE;
|
|
942
619
|
}
|
|
943
620
|
```
|
|
944
621
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
Use this method to delete an existing template.
|
|
948
|
-
|
|
949
|
-
```ts
|
|
950
|
-
const deleteResult = await ekascribe.deleteTemplate('template-123');
|
|
951
|
-
```
|
|
952
|
-
|
|
953
|
-
- #### Response type:
|
|
622
|
+
---
|
|
954
623
|
|
|
955
|
-
|
|
956
|
-
{
|
|
957
|
-
code: number;
|
|
958
|
-
msg: string;
|
|
959
|
-
template_id?: string;
|
|
960
|
-
message?: string;
|
|
961
|
-
error?: { code: string; message: string };
|
|
962
|
-
}
|
|
963
|
-
```
|
|
624
|
+
## Session Utils
|
|
964
625
|
|
|
965
|
-
###
|
|
626
|
+
### `getSessionHistory(request)`
|
|
966
627
|
|
|
967
|
-
|
|
628
|
+
Fetch previous sessions.
|
|
968
629
|
|
|
969
630
|
```ts
|
|
970
|
-
const
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
const aiTemplate = await ekascribe.aiGenerateTemplate(formData);
|
|
631
|
+
const sessions = await ekascribe.sessions.getSessionHistory({
|
|
632
|
+
txn_count: 10, // number of sessions to fetch
|
|
633
|
+
oid: 'patient-oid', // optional: filter by patient oid
|
|
634
|
+
});
|
|
976
635
|
```
|
|
977
636
|
|
|
978
|
-
|
|
637
|
+
#### Response: `TGetTransactionHistoryResponse`
|
|
979
638
|
|
|
980
639
|
```ts
|
|
981
640
|
{
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
];
|
|
641
|
+
data: Array<{
|
|
642
|
+
txn_id: string;
|
|
643
|
+
b_id: string;
|
|
644
|
+
created_at: string;
|
|
645
|
+
mode: string;
|
|
646
|
+
oid: string;
|
|
647
|
+
patient_details?: TPatientDetails;
|
|
648
|
+
processing_status: 'success' | 'system_failure' | 'request_failure' | 'cancelled' | 'in-progress';
|
|
649
|
+
user_status: 'init' | 'commit';
|
|
650
|
+
uuid: string;
|
|
651
|
+
}>;
|
|
652
|
+
status: string;
|
|
995
653
|
code: number;
|
|
996
654
|
message: string;
|
|
655
|
+
retrieved_count: number;
|
|
997
656
|
}
|
|
998
657
|
```
|
|
999
658
|
|
|
1000
|
-
###
|
|
659
|
+
### `getSessionDetails(request)`
|
|
1001
660
|
|
|
1002
|
-
|
|
661
|
+
Get detailed information about a specific session, including documents, context, and presigned URLs.
|
|
1003
662
|
|
|
1004
663
|
```ts
|
|
1005
|
-
const
|
|
1006
|
-
|
|
664
|
+
const details = await ekascribe.sessions.getSessionDetails({
|
|
665
|
+
session_id: 'session-id',
|
|
666
|
+
presigned: true, // include presigned URLs for documents
|
|
1007
667
|
});
|
|
1008
668
|
```
|
|
1009
669
|
|
|
1010
|
-
|
|
670
|
+
Each document in the response contains a `presigned_url`. To get the actual document content (notes, transcript, etc.), you need to fetch it from this URL:
|
|
1011
671
|
|
|
1012
672
|
```ts
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
scribe_enabled?: boolean;
|
|
1018
|
-
msg: string;
|
|
1019
|
-
code: number;
|
|
1020
|
-
error?: { code: string; message: string };
|
|
673
|
+
const doc = details.data?.documents[0];
|
|
674
|
+
if (doc?.presigned_url) {
|
|
675
|
+
const response = await fetch(doc.presigned_url);
|
|
676
|
+
const content = await response.json();
|
|
1021
677
|
}
|
|
1022
678
|
```
|
|
1023
679
|
|
|
1024
|
-
|
|
680
|
+
> Presigned URLs are temporary — check `presigned_url_expires_at` (epoch timestamp) before using. Call `getDocument(documentId)` to get a fresh presigned URL if expired.
|
|
1025
681
|
|
|
1026
|
-
|
|
682
|
+
#### Request
|
|
1027
683
|
|
|
1028
684
|
```ts
|
|
1029
|
-
|
|
685
|
+
type TGetV1SessionDetailsRequest = {
|
|
686
|
+
session_id: string;
|
|
687
|
+
presigned?: boolean; // default: false
|
|
688
|
+
};
|
|
1030
689
|
```
|
|
1031
690
|
|
|
1032
|
-
|
|
691
|
+
#### Response: `TGetV1SessionDetailsResponse`
|
|
1033
692
|
|
|
1034
693
|
```ts
|
|
1035
|
-
{
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
694
|
+
type TGetV1SessionDetailsResponse = {
|
|
695
|
+
data?: {
|
|
696
|
+
schema_version: string;
|
|
697
|
+
session_id: string;
|
|
698
|
+
uuid: string;
|
|
699
|
+
wid: string;
|
|
700
|
+
created_at: number;
|
|
701
|
+
expires_at: number;
|
|
702
|
+
upload_url: string;
|
|
703
|
+
status: string;
|
|
704
|
+
user_status: 'init' | 'recording_started' | 'commit' | string;
|
|
705
|
+
transfer: string;
|
|
706
|
+
flavour: string;
|
|
707
|
+
patient_details: TPatientDetails | Record<string, unknown>;
|
|
708
|
+
audio_matrix: Record<string, unknown>;
|
|
709
|
+
additional_data: {
|
|
710
|
+
input_languages?: { id: string; name: string }[];
|
|
711
|
+
output_format_template?: {
|
|
712
|
+
template_id: string;
|
|
713
|
+
template_name?: string;
|
|
714
|
+
template_type?: string;
|
|
715
|
+
}[];
|
|
716
|
+
[key: string]: unknown;
|
|
717
|
+
};
|
|
718
|
+
documents: Array<{
|
|
719
|
+
document_id: string;
|
|
720
|
+
session_id: string;
|
|
721
|
+
template_id: string;
|
|
722
|
+
document_name: string;
|
|
723
|
+
document_type: 'notes' | 'context' | 'transcript' | 'integration';
|
|
724
|
+
type: string;
|
|
725
|
+
status: string;
|
|
726
|
+
errors: Array<{ type: string | null; code: string; msg: string }>;
|
|
727
|
+
warnings: Array<{ type: string | null; code: string; msg: string }>;
|
|
728
|
+
publish: Record<string, unknown>;
|
|
729
|
+
created_at: number;
|
|
730
|
+
presigned_url: string | null;
|
|
731
|
+
presigned_url_expires_at: number | null;
|
|
732
|
+
vault_doc_id: string | null;
|
|
733
|
+
lang?: string;
|
|
734
|
+
}>;
|
|
735
|
+
context: {
|
|
736
|
+
past_sessions?: Array<{ date_epoch: number; session_id: string }>;
|
|
737
|
+
documents?: string[];
|
|
738
|
+
attachments?: Array<{ id: string; patient_oid?: string }>;
|
|
739
|
+
};
|
|
740
|
+
};
|
|
741
|
+
status_code: number;
|
|
742
|
+
message?: string;
|
|
743
|
+
};
|
|
1050
744
|
```
|
|
1051
745
|
|
|
1052
|
-
###
|
|
746
|
+
### `getDocument(documentId)`
|
|
1053
747
|
|
|
1054
|
-
Use this
|
|
748
|
+
Fetch a single document by ID. Use this to get a fresh presigned URL if the previous one has expired.
|
|
1055
749
|
|
|
1056
750
|
```ts
|
|
1057
|
-
const
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
example: 'Patient presents with chest pain for 2 days',
|
|
1062
|
-
});
|
|
1063
|
-
```
|
|
1064
|
-
|
|
1065
|
-
- #### Response type:
|
|
1066
|
-
|
|
1067
|
-
```ts
|
|
1068
|
-
{
|
|
1069
|
-
msg: string;
|
|
1070
|
-
section_id: string;
|
|
1071
|
-
code: number;
|
|
1072
|
-
action: 'updated' | 'created_custom';
|
|
1073
|
-
error?: { code: string; message: string };
|
|
751
|
+
const doc = await ekascribe.documents.getDocument('document-id');
|
|
752
|
+
if (doc.data?.presigned_url) {
|
|
753
|
+
const response = await fetch(doc.data.presigned_url);
|
|
754
|
+
const content = await response.json();
|
|
1074
755
|
}
|
|
1075
756
|
```
|
|
1076
757
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
Use this method to update an existing template section.
|
|
1080
|
-
|
|
1081
|
-
```ts
|
|
1082
|
-
const updatedSection = await ekascribe.updateTemplateSection({
|
|
1083
|
-
section_id: 'section-123',
|
|
1084
|
-
title: 'Updated Chief Complaint',
|
|
1085
|
-
desc: 'Updated description',
|
|
1086
|
-
format: 'B',
|
|
1087
|
-
example: 'Updated example text',
|
|
1088
|
-
});
|
|
1089
|
-
```
|
|
1090
|
-
|
|
1091
|
-
- #### Response type:
|
|
758
|
+
#### Response: `TPostV1DocumentResponse`
|
|
1092
759
|
|
|
1093
760
|
```ts
|
|
1094
|
-
{
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
761
|
+
type TPostV1DocumentResponse = {
|
|
762
|
+
status_code: number;
|
|
763
|
+
status?: string;
|
|
764
|
+
message?: string;
|
|
765
|
+
data?: {
|
|
766
|
+
document_id: string;
|
|
767
|
+
session_id: string;
|
|
768
|
+
template_id: string;
|
|
769
|
+
document_name: string;
|
|
770
|
+
type: string;
|
|
771
|
+
status: string;
|
|
772
|
+
errors: unknown[];
|
|
773
|
+
warnings: unknown[];
|
|
774
|
+
usage_information: Record<string, unknown>;
|
|
775
|
+
document_path: {
|
|
776
|
+
bucket: string;
|
|
777
|
+
folder: string;
|
|
778
|
+
filename: string;
|
|
779
|
+
};
|
|
780
|
+
presigned_url: string;
|
|
781
|
+
created_at: string;
|
|
782
|
+
updated_at: number;
|
|
783
|
+
publish: Record<string, unknown>;
|
|
784
|
+
};
|
|
785
|
+
};
|
|
1101
786
|
```
|
|
1102
787
|
|
|
1103
|
-
###
|
|
1104
|
-
|
|
1105
|
-
Use this method to delete a template section.
|
|
1106
|
-
|
|
1107
|
-
```ts
|
|
1108
|
-
const deleteResult = await ekascribe.deleteTemplateSection('section-123');
|
|
1109
|
-
```
|
|
788
|
+
### `patchSessionStatus(request, sessionId?)`
|
|
1110
789
|
|
|
1111
|
-
|
|
790
|
+
Update session properties (patient details, status, templates).
|
|
1112
791
|
|
|
1113
792
|
```ts
|
|
1114
|
-
{
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
error?: { code: string; message: string };
|
|
1120
|
-
}
|
|
793
|
+
await ekascribe.sessions.patchSessionStatus({
|
|
794
|
+
patient_details: { name: 'Jane Doe', age: '30', gender: 'female' },
|
|
795
|
+
additional_data: { notes: 'Follow-up visit' },
|
|
796
|
+
templates: ['soap', 'prescription'],
|
|
797
|
+
}, sessionId);
|
|
1121
798
|
```
|
|
1122
799
|
|
|
1123
|
-
|
|
800
|
+
---
|
|
1124
801
|
|
|
1125
|
-
|
|
802
|
+
## Discovery
|
|
1126
803
|
|
|
1127
|
-
|
|
804
|
+
### `getDiscoveryDocument()`
|
|
1128
805
|
|
|
1129
|
-
|
|
1130
|
-
- Uploads audio files to S3 via presigned URL
|
|
1131
|
-
- Initializes a transaction with the uploaded files
|
|
1132
|
-
- Returns the transaction details
|
|
806
|
+
Get the raw discovery document fetched during initialization. Contains server capabilities (supported models, languages, upload methods, audio formats).
|
|
1133
807
|
|
|
1134
808
|
```ts
|
|
1135
|
-
const
|
|
1136
|
-
const audioFileNames = ['audio1.mp3', 'audio2.mp3'];
|
|
1137
|
-
|
|
1138
|
-
const response = await ekascribe.uploadAudioWithPresignedUrl({
|
|
1139
|
-
action: 'ekascribe-v2', // Pass this exact value without changing
|
|
1140
|
-
audioFiles,
|
|
1141
|
-
audioFileNames,
|
|
1142
|
-
mode: 'consultation',
|
|
1143
|
-
txn_id: 'unique-transaction-id',
|
|
1144
|
-
input_language: ['en-IN'],
|
|
1145
|
-
output_format_template: [{ template_id: 'your_template_id' }],
|
|
1146
|
-
transfer: 'non-vaded', // Use 'non-vaded' for raw audio files
|
|
1147
|
-
model_type: 'pro' | 'lite',
|
|
1148
|
-
system_info: {
|
|
1149
|
-
platform: 'web',
|
|
1150
|
-
language: 'en',
|
|
1151
|
-
time_zone: 'Asia/Kolkata',
|
|
1152
|
-
},
|
|
1153
|
-
patient_details: {
|
|
1154
|
-
username: 'John Doe',
|
|
1155
|
-
age: 35,
|
|
1156
|
-
biologicalSex: 'M',
|
|
1157
|
-
},
|
|
1158
|
-
version: '1.0.0',
|
|
1159
|
-
additional_data: {},
|
|
1160
|
-
});
|
|
809
|
+
const discovery = ekascribe.sessions.getDiscoveryDocument();
|
|
1161
810
|
```
|
|
1162
811
|
|
|
1163
|
-
|
|
812
|
+
Returns `DiscoveryDocument | null`.
|
|
1164
813
|
|
|
1165
|
-
|
|
1166
|
-
- `audioFiles`: Array of File or Blob objects
|
|
1167
|
-
- `audioFileNames`: Array of file names corresponding to audio files
|
|
1168
|
-
- `transfer`: Use `non-vaded` for raw audio files (not processed with VAD)
|
|
1169
|
-
- Other parameters: Same as `initTransaction` (see step 3)
|
|
814
|
+
### `getDiscoveryConfig()`
|
|
1170
815
|
|
|
1171
|
-
|
|
816
|
+
Get the resolved configuration derived from the discovery document.
|
|
1172
817
|
|
|
1173
818
|
```ts
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
819
|
+
const config = ekascribe.sessions.getDiscoveryConfig();
|
|
820
|
+
|
|
821
|
+
if (config.success) {
|
|
822
|
+
console.log('Resolved config:', config.data);
|
|
1178
823
|
}
|
|
1179
824
|
```
|
|
1180
825
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
Possible Error Codes in `error_code`:
|
|
826
|
+
Returns `SDKResult<ResolvedConfig>`.
|
|
1184
827
|
|
|
1185
|
-
|
|
1186
|
-
- `audio_upload_failed`: Failed to upload audio files to S3, retry with the same method
|
|
1187
|
-
- `txn_limit_exceeded`: Maximum number of transactions exceeded
|
|
1188
|
-
- `txn_init_failed`: Failed to initialize transaction after upload, retry with the same method
|
|
828
|
+
---
|
|
1189
829
|
|
|
1190
|
-
|
|
830
|
+
## Widget
|
|
1191
831
|
|
|
1192
|
-
|
|
832
|
+
The SDK provides an optional pre-built recording UI. When enabled, the SDK injects a floating widget into your page via Shadow DOM — you write zero UI code.
|
|
1193
833
|
|
|
1194
|
-
|
|
1195
|
-
// Update tokens in your config variable
|
|
1196
|
-
sdkConfig.access_token = '<new_access_token>';
|
|
834
|
+
### Integration
|
|
1197
835
|
|
|
1198
|
-
|
|
1199
|
-
ekascribe.updateAuthTokens({ access_token: sdkConfig.access_token });
|
|
836
|
+
**Step 1:** Enable the widget in SDK config with session defaults and callbacks:
|
|
1200
837
|
|
|
1201
|
-
|
|
1202
|
-
const
|
|
838
|
+
```ts
|
|
839
|
+
const ekascribe = getEkaScribeInstance({
|
|
840
|
+
access_token: token,
|
|
841
|
+
env: 'PROD',
|
|
842
|
+
allianceConfig: { baseUrl: '...' },
|
|
843
|
+
widget: {
|
|
844
|
+
enabled: true,
|
|
845
|
+
orientation: 'horizontal', // 'horizontal' | 'vertical'
|
|
846
|
+
zIndex: 9999, // optional
|
|
847
|
+
position: { bottom: 20, right: 20 }, // optional
|
|
848
|
+
sessionDefaults: {
|
|
849
|
+
input_language: ['en'],
|
|
850
|
+
output_format_template: [{ template_id: 'soap' }],
|
|
851
|
+
model_type: 'pro',
|
|
852
|
+
mode: 'consultation',
|
|
853
|
+
},
|
|
854
|
+
callbacks: {
|
|
855
|
+
onRecordingStart: ({ txn_id }) => {},
|
|
856
|
+
onRecordingStop: ({ txn_id, duration }) => {},
|
|
857
|
+
onProcessingComplete: ({ txn_id, sessionData }) => {
|
|
858
|
+
// sessionData contains templates, transcript, etc.
|
|
859
|
+
},
|
|
860
|
+
onError: ({ error_code, message }) => {},
|
|
861
|
+
},
|
|
862
|
+
},
|
|
863
|
+
});
|
|
1203
864
|
```
|
|
1204
865
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
### 1. Get total uploaded files
|
|
1208
|
-
|
|
1209
|
-
Use this method to retrieve all the audio files generated for a specific session.
|
|
866
|
+
**Step 2:** Call `startForPatient()` for each patient — the widget appears and the user interacts with it directly (pause, resume, stop). You receive results via callbacks.
|
|
1210
867
|
|
|
1211
868
|
```ts
|
|
1212
|
-
|
|
869
|
+
await ekascribe.startForPatient({
|
|
870
|
+
txn_id: 'unique-session-id',
|
|
871
|
+
patient_details: { // optional
|
|
872
|
+
username: 'John Doe',
|
|
873
|
+
age: 45,
|
|
874
|
+
biologicalSex: 'M',
|
|
875
|
+
},
|
|
876
|
+
additional_data: {}, // optional
|
|
877
|
+
});
|
|
1213
878
|
```
|
|
1214
879
|
|
|
1215
|
-
|
|
880
|
+
That's it. The widget handles `startRecordingV2()`, `pauseRecording()`, `resumeRecording()`, `endRecording()`, and `getSessionStatus()` internally.
|
|
1216
881
|
|
|
1217
|
-
|
|
1218
|
-
['1.mp3', '2.mp3', '3.mp3', '4.mp3'];
|
|
1219
|
-
```
|
|
882
|
+
### Behavior
|
|
1220
883
|
|
|
1221
|
-
|
|
884
|
+
- **Draggable** — the widget can be repositioned anywhere on screen.
|
|
885
|
+
- **One recording at a time** — calling `startForPatient()` while a recording is active returns an error. If the widget is in DONE or ERROR state, it auto-resets and starts the new session.
|
|
886
|
+
- **Done state** — after processing, the widget expands to show transcript and rendered markdown notes. If both are available, they appear in separate tabs.
|
|
1222
887
|
|
|
1223
|
-
|
|
888
|
+
### Widget State Flow
|
|
1224
889
|
|
|
1225
|
-
```
|
|
1226
|
-
|
|
890
|
+
```
|
|
891
|
+
COLLAPSED ──> RECORDING ──> PAUSED ──> RECORDING ──> PROCESSING ──> DONE
|
|
892
|
+
^ │ │ │
|
|
893
|
+
│ └──── (user clicks stop) ───────────────┘ │
|
|
894
|
+
│ │
|
|
895
|
+
└──────────── (user clicks close) ────────────────────────────────┘
|
|
896
|
+
│
|
|
897
|
+
ERROR
|
|
1227
898
|
```
|
|
1228
899
|
|
|
1229
|
-
|
|
900
|
+
### Types
|
|
1230
901
|
|
|
1231
902
|
```ts
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
903
|
+
interface WidgetConfig {
|
|
904
|
+
enabled: boolean;
|
|
905
|
+
theme?: 'light' | 'dark';
|
|
906
|
+
zIndex?: number;
|
|
907
|
+
primaryColor?: string;
|
|
908
|
+
position?: { bottom?: number; right?: number; top?: number; left?: number };
|
|
909
|
+
orientation?: 'horizontal' | 'vertical';
|
|
910
|
+
callbacks?: WidgetCallbacks;
|
|
911
|
+
sessionDefaults: {
|
|
912
|
+
input_language: string[];
|
|
913
|
+
output_format_template: { template_id: string; template_name?: string; template_type?: string }[];
|
|
914
|
+
model_type: string;
|
|
915
|
+
mode: string;
|
|
916
|
+
};
|
|
917
|
+
}
|
|
1236
918
|
|
|
1237
|
-
|
|
919
|
+
interface StartForPatientConfig {
|
|
920
|
+
txn_id: string;
|
|
921
|
+
patient_details?: {
|
|
922
|
+
username?: string;
|
|
923
|
+
age?: number;
|
|
924
|
+
biologicalSex?: string;
|
|
925
|
+
mobile?: string;
|
|
926
|
+
};
|
|
927
|
+
additional_data?: Record<string, unknown>;
|
|
928
|
+
}
|
|
1238
929
|
|
|
1239
|
-
|
|
1240
|
-
|
|
930
|
+
interface WidgetCallbacks {
|
|
931
|
+
onRecordingStart?: (data: { txn_id: string }) => void;
|
|
932
|
+
onRecordingPause?: (data: { txn_id: string; duration: number }) => void;
|
|
933
|
+
onRecordingResume?: (data: { txn_id: string }) => void;
|
|
934
|
+
onRecordingStop?: (data: { txn_id: string; duration: number }) => void;
|
|
935
|
+
onProcessingStart?: (data: { txn_id: string }) => void;
|
|
936
|
+
onProcessingComplete?: (data: { txn_id: string; sessionData: unknown }) => void;
|
|
937
|
+
onError?: (data: { error_code: string; message: string }) => void;
|
|
938
|
+
onWidgetClose?: (data: { txn_id: string }) => void;
|
|
939
|
+
}
|
|
1241
940
|
```
|
|
1242
941
|
|
|
1243
|
-
|
|
942
|
+
---
|
|
1244
943
|
|
|
1245
|
-
|
|
1246
|
-
['1.mp3', '2.mp3'];
|
|
1247
|
-
```
|
|
944
|
+
## Authentication
|
|
1248
945
|
|
|
1249
|
-
###
|
|
946
|
+
### `updateAuthTokens(token)`
|
|
1250
947
|
|
|
1251
|
-
|
|
948
|
+
Manually update the access token. This propagates the token to all internal transports and the worker.
|
|
1252
949
|
|
|
1253
950
|
```ts
|
|
1254
|
-
|
|
951
|
+
ekascribe.updateAuthTokens({ access_token: 'new-token' });
|
|
1255
952
|
```
|
|
1256
953
|
|
|
1257
|
-
|
|
954
|
+
> If you have `onTokenRequired` registered, the SDK handles 401s automatically. You only need `updateAuthTokens()` for proactive token rotation (e.g., before expiry).
|
|
1258
955
|
|
|
1259
|
-
|
|
956
|
+
---
|
|
1260
957
|
|
|
1261
|
-
|
|
1262
|
-
ekaScribe.reinitializeVad();
|
|
1263
|
-
```
|
|
958
|
+
## SharedWorker Configuration
|
|
1264
959
|
|
|
1265
|
-
|
|
960
|
+
The SDK offloads audio compression and upload to a SharedWorker for better main-thread performance. If SharedWorker is unavailable or fails, the SDK silently falls back to main-thread processing.
|
|
1266
961
|
|
|
1267
|
-
|
|
962
|
+
### Setup
|
|
1268
963
|
|
|
1269
964
|
```ts
|
|
1270
|
-
|
|
1271
|
-
```
|
|
965
|
+
import { createWorkerBlobUrl } from '@eka-care/ekascribe-ts-sdk';
|
|
1272
966
|
|
|
1273
|
-
|
|
967
|
+
// Option 1: Use the built-in helper (recommended)
|
|
968
|
+
const workerUrl = await createWorkerBlobUrl();
|
|
1274
969
|
|
|
1275
|
-
|
|
970
|
+
// Option 2: Fetch from CDN
|
|
971
|
+
async function getWorkerUrl() {
|
|
972
|
+
const res = await fetch(
|
|
973
|
+
'https://cdn.jsdelivr.net/npm/@eka-care/ekascribe-ts-sdk@latest/dist/worker.bundle.js'
|
|
974
|
+
);
|
|
975
|
+
const script = await res.text();
|
|
976
|
+
const blob = new Blob([script], { type: 'application/javascript' });
|
|
977
|
+
return URL.createObjectURL(blob);
|
|
978
|
+
}
|
|
979
|
+
const workerUrl = await getWorkerUrl();
|
|
1276
980
|
|
|
1277
|
-
|
|
1278
|
-
|
|
981
|
+
// Option 3: Copy to public directory
|
|
982
|
+
// cp node_modules/@eka-care/ekascribe-ts-sdk/dist/worker.bundle.js public/
|
|
983
|
+
const workerUrl = '/worker.bundle.js';
|
|
1279
984
|
```
|
|
1280
985
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
Use this method to update the access token when it expires (e.g., when you receive a 401 error).
|
|
986
|
+
Pass the URL in config:
|
|
1284
987
|
|
|
1285
988
|
```ts
|
|
1286
|
-
ekascribe
|
|
989
|
+
const ekascribe = getEkaScribeInstance({
|
|
990
|
+
// ...
|
|
991
|
+
sharedWorkerUrl: workerUrl,
|
|
992
|
+
});
|
|
1287
993
|
```
|
|
1288
994
|
|
|
1289
|
-
**
|
|
995
|
+
**Notes:**
|
|
1290
996
|
|
|
1291
|
-
-
|
|
1292
|
-
-
|
|
1293
|
-
-
|
|
1294
|
-
|
|
1295
|
-
### 9. Get Doctor Header and Footer
|
|
997
|
+
- The worker URL must be accessible from the same origin or have proper CORS headers
|
|
998
|
+
- Remember to revoke blob URLs when done: `URL.revokeObjectURL(workerUrl)`
|
|
999
|
+
- If `sharedWorkerUrl` is not provided, the SDK uses the main thread (no SharedWorker)
|
|
1296
1000
|
|
|
1297
|
-
|
|
1001
|
+
---
|
|
1298
1002
|
|
|
1299
|
-
|
|
1300
|
-
const headerFooter = await ekascribe.getDoctorHeaderFooter({
|
|
1301
|
-
doctor_oid: '161459684229004',
|
|
1302
|
-
clinic_id: '60532c7fcb46901ba3a3e477', // optional
|
|
1303
|
-
});
|
|
1304
|
-
```
|
|
1003
|
+
## Error Codes
|
|
1305
1004
|
|
|
1306
|
-
|
|
1005
|
+
| Error Code | Description |
|
|
1006
|
+
|---|---|
|
|
1007
|
+
| `microphone` | Microphone access error (permission denied or unavailable) |
|
|
1008
|
+
| `txn_init_failed` | Failed to initialize session |
|
|
1009
|
+
| `txn_limit_exceeded` | Maximum concurrent sessions exceeded |
|
|
1010
|
+
| `internal_server_error` | Unexpected server-side error |
|
|
1011
|
+
| `end_recording_failed` | Failed to end recording |
|
|
1012
|
+
| `audio_upload_failed` | Audio file upload to server failed |
|
|
1013
|
+
| `txn_commit_failed` | Commit call failed |
|
|
1014
|
+
| `txn_status_mismatch` | Invalid operation for current session state |
|
|
1015
|
+
| `network_error` | Network connectivity issue |
|
|
1016
|
+
| `unknown_error` | Unclassified error |
|
|
1017
|
+
| `unauthorized` | Authentication failed (invalid or expired token) |
|
|
1018
|
+
| `forbidden` | Insufficient permissions |
|
|
1307
1019
|
|
|
1308
|
-
|
|
1309
|
-
- `clinic_id`: The clinic ID (optional) - if provided, returns header/footer for this specific clinic
|
|
1020
|
+
---
|
|
1310
1021
|
|
|
1311
|
-
|
|
1022
|
+
## Deprecated Methods
|
|
1312
1023
|
|
|
1313
|
-
|
|
1314
|
-
2. Otherwise, looks for the doctor's default clinic and returns its header/footer
|
|
1315
|
-
3. If no matching template is found, returns `null`
|
|
1024
|
+
These methods are from the older SDK version. They still work but are not recommended for new integrations.
|
|
1316
1025
|
|
|
1317
|
-
|
|
1026
|
+
| Deprecated Method | Use Instead |
|
|
1027
|
+
|---|---|
|
|
1028
|
+
| `initTransaction()` + `startRecording()` | `startRecordingV2()` |
|
|
1029
|
+
| `getTemplateOutput()` | `getSessionStatus()` with polling |
|
|
1030
|
+
| `getOutputTranscription()` | `getSessionStatus()` with polling |
|
|
1031
|
+
| `commitTransactionCall()` | Handled automatically by `endRecording()` |
|
|
1032
|
+
| `stopTransactionCall()` | Handled automatically by `endRecording()` |
|
|
1318
1033
|
|
|
1319
|
-
|
|
1320
|
-
{
|
|
1321
|
-
data: {
|
|
1322
|
-
_id: string | null; // Template ID
|
|
1323
|
-
clinic_id: string | null; // Clinic ID
|
|
1324
|
-
doctor_id: string | null; // Doctor ID
|
|
1325
|
-
type: string | null; // Template type (e.g., "PRINT")
|
|
1326
|
-
header_img: string | null; // URL of the header image
|
|
1327
|
-
header_height: string | null; // Height of the header (e.g., "5cm")
|
|
1328
|
-
header_top_margin: string | null; // Top margin for header (e.g., "0.5cm")
|
|
1329
|
-
footer_img: string | null; // URL of the footer image
|
|
1330
|
-
footer_height: string | null; // Height of the footer (e.g., "6.7cm")
|
|
1331
|
-
margin_left: string | null; // Left margin (e.g., "1.27cm")
|
|
1332
|
-
margin_right: string | null; // Right margin (e.g., "1.27cm")
|
|
1333
|
-
page_size: string | null; // Page size (e.g., "A4")
|
|
1334
|
-
show_eka_logo: boolean | null; // Show Eka logo on prescription
|
|
1335
|
-
show_name_in_signature: boolean | null; // Show name in signature
|
|
1336
|
-
show_not_valid_for_medical_legal_purpose_message: boolean | null; // Show disclaimer message
|
|
1337
|
-
show_page_number: boolean | null; // Show page numbers
|
|
1338
|
-
show_prescription_id: boolean | null; // Show prescription ID
|
|
1339
|
-
show_signature: boolean | null; // Show signature
|
|
1340
|
-
} | null;
|
|
1341
|
-
code: number;
|
|
1342
|
-
message?: string;
|
|
1343
|
-
}
|
|
1344
|
-
```
|
|
1034
|
+
### `initTransaction(request)`
|
|
1345
1035
|
|
|
1346
|
-
|
|
1036
|
+
Creates a session on the server. Must be followed by `startRecording()`.
|
|
1347
1037
|
|
|
1348
1038
|
```ts
|
|
1349
|
-
{
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
margin_left: "1.27cm",
|
|
1361
|
-
margin_right: "1.27cm",
|
|
1362
|
-
page_size: "A4",
|
|
1363
|
-
show_eka_logo: true,
|
|
1364
|
-
show_name_in_signature: true,
|
|
1365
|
-
show_not_valid_for_medical_legal_purpose_message: true,
|
|
1366
|
-
show_page_number: true,
|
|
1367
|
-
show_prescription_id: true,
|
|
1368
|
-
show_signature: true
|
|
1039
|
+
const result = await ekascribe.initTransaction({
|
|
1040
|
+
mode: 'consultation',
|
|
1041
|
+
input_language: ['en-IN'],
|
|
1042
|
+
output_format_template: [{ template_id: 'template-id' }],
|
|
1043
|
+
txn_id: 'unique-id',
|
|
1044
|
+
transfer: 'chunked',
|
|
1045
|
+
model_type: 'pro',
|
|
1046
|
+
patient_details: { // optional
|
|
1047
|
+
username: 'John Doe',
|
|
1048
|
+
age: 45,
|
|
1049
|
+
biologicalSex: 'M',
|
|
1369
1050
|
},
|
|
1370
|
-
|
|
1371
|
-
}
|
|
1051
|
+
});
|
|
1052
|
+
// result: { status_code, message, txn_id?, error_code? }
|
|
1372
1053
|
```
|
|
1373
1054
|
|
|
1374
|
-
###
|
|
1055
|
+
### `startRecording(microphoneID?)`
|
|
1375
1056
|
|
|
1376
|
-
|
|
1057
|
+
Starts recording for an already initialized session.
|
|
1377
1058
|
|
|
1378
1059
|
```ts
|
|
1379
|
-
ekascribe.
|
|
1060
|
+
const result = await ekascribe.startRecording();
|
|
1061
|
+
// result: { status_code, message, txn_id?, error_code? }
|
|
1380
1062
|
```
|
|
1381
1063
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
### 11. Configure VAD Constants
|
|
1064
|
+
### `getTemplateOutput(request)`
|
|
1385
1065
|
|
|
1386
|
-
|
|
1066
|
+
Fetches processed template output for a session.
|
|
1387
1067
|
|
|
1388
1068
|
```ts
|
|
1389
|
-
ekascribe.
|
|
1390
|
-
|
|
1391
|
-
desp_length: 5, // Desperation chunk length in seconds
|
|
1392
|
-
max_length: 30, // Maximum chunk length in seconds
|
|
1393
|
-
sr: 16000, // Sample rate in Hz
|
|
1394
|
-
frame_size: 512, // Frame size in samples
|
|
1395
|
-
pre_speech_pad_frames: 10, // Pre-speech padding frames count
|
|
1396
|
-
short_thsld: 0.5, // Short silence threshold in seconds
|
|
1397
|
-
long_thsld: 0.8, // Long silence threshold in seconds
|
|
1398
|
-
});
|
|
1069
|
+
const result = await ekascribe.getTemplateOutput({ txn_id: 'session-id' });
|
|
1070
|
+
// result: { status_code, message?, response? }
|
|
1399
1071
|
```
|
|
1400
1072
|
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
- `pref_length`: Preferred audio chunk length before clipping (in seconds)
|
|
1404
|
-
- `desp_length`: Desperation length - will clip even if not ideal (in seconds)
|
|
1405
|
-
- `max_length`: Maximum audio chunk length (in seconds)
|
|
1406
|
-
- `sr`: Sample rate in Hz (typically 16000)
|
|
1407
|
-
- `frame_size`: VAD frame size in samples
|
|
1408
|
-
- `pre_speech_pad_frames`: Number of frames to include before speech starts
|
|
1409
|
-
- `short_thsld`: Short silence threshold for clipping decisions (in seconds)
|
|
1410
|
-
- `long_thsld`: Long silence threshold for clipping decisions (in seconds)
|
|
1073
|
+
### `getOutputTranscription(request)`
|
|
1411
1074
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
Use this method to terminate the shared worker instance and free up resources.
|
|
1075
|
+
Fetches the transcription output for a session.
|
|
1415
1076
|
|
|
1416
1077
|
```ts
|
|
1417
|
-
await ekascribe.
|
|
1078
|
+
const result = await ekascribe.getOutputTranscription({ txn_id: 'session-id' });
|
|
1079
|
+
// result: { status_code, message?, response? }
|
|
1418
1080
|
```
|
|
1419
1081
|
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
- When cleaning up resources after the SDK is no longer needed
|
|
1423
|
-
- Before reinitializing with a new shared worker configuration
|
|
1082
|
+
### `commitTransactionCall()`
|
|
1424
1083
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
### 1. Event callback
|
|
1428
|
-
|
|
1429
|
-
This callback provides information about SDK operations. Use it to monitor file uploads, transaction status, AWS configuration, and authentication errors.
|
|
1084
|
+
Commits the current transaction on the server.
|
|
1430
1085
|
|
|
1431
1086
|
```ts
|
|
1432
|
-
ekascribe.
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
// Handle different callback types
|
|
1436
|
-
switch (eventData.callback_type) {
|
|
1437
|
-
case 'file_upload_status':
|
|
1438
|
-
// Track audio chunk upload progress
|
|
1439
|
-
console.log(`Uploaded ${eventData.data?.success}/${eventData.data?.total} chunks`);
|
|
1440
|
-
break;
|
|
1441
|
-
case 'transaction_status':
|
|
1442
|
-
// Monitor transaction lifecycle (init, stop, commit, cancel)
|
|
1443
|
-
console.log('Transaction update:', eventData.message);
|
|
1444
|
-
break;
|
|
1445
|
-
case 'aws_configure_status':
|
|
1446
|
-
// AWS S3 configuration status
|
|
1447
|
-
console.log('AWS config:', eventData.status);
|
|
1448
|
-
break;
|
|
1449
|
-
case 'authentication_status':
|
|
1450
|
-
// API authentication errors
|
|
1451
|
-
console.error('Auth error:', eventData.message);
|
|
1452
|
-
break;
|
|
1453
|
-
}
|
|
1454
|
-
});
|
|
1087
|
+
const result = await ekascribe.commitTransactionCall();
|
|
1088
|
+
// result: { status_code, message, error_code?, failed_files? }
|
|
1455
1089
|
```
|
|
1456
1090
|
|
|
1457
|
-
|
|
1091
|
+
### `stopTransactionCall()`
|
|
1092
|
+
|
|
1093
|
+
Stops the current transaction on the server.
|
|
1458
1094
|
|
|
1459
1095
|
```ts
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
status: 'success' | 'error' | 'info',
|
|
1463
|
-
message: string,
|
|
1464
|
-
timestamp: string, // ISO timestamp
|
|
1465
|
-
error?: {
|
|
1466
|
-
code: number,
|
|
1467
|
-
msg: string,
|
|
1468
|
-
details: any
|
|
1469
|
-
},
|
|
1470
|
-
data?: {
|
|
1471
|
-
success?: number, // Number of successfully uploaded chunks
|
|
1472
|
-
total?: number, // Total number of chunks
|
|
1473
|
-
is_uploaded?: boolean, // Whether current chunk uploaded successfully
|
|
1474
|
-
fileName?: string, // Current file name
|
|
1475
|
-
chunkData?: Uint8Array[], // Audio chunk data for current audiofile
|
|
1476
|
-
request?: any, // API request details
|
|
1477
|
-
response?: any // API response details
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1096
|
+
const result = await ekascribe.stopTransactionCall();
|
|
1097
|
+
// result: { status_code, message, error_code? }
|
|
1480
1098
|
```
|
|
1481
1099
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
**`file_upload_status`** - Track audio chunk upload progress
|
|
1485
|
-
|
|
1486
|
-
Use this to monitor upload progress of audio chunks:
|
|
1487
|
-
|
|
1488
|
-
- `status: 'info'` - Audio chunk info
|
|
1100
|
+
---
|
|
1489
1101
|
|
|
1490
|
-
|
|
1491
|
-
- `data.total`: Total chunks generated
|
|
1492
|
-
- `data.fileName`: Current chunk file name
|
|
1493
|
-
- `data.chunkData`: Audio data for current chunk
|
|
1102
|
+
## Full Example
|
|
1494
1103
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
- `status: 'error'` - Chunk upload failed
|
|
1502
|
-
|
|
1503
|
-
- `error.code`: HTTP error code
|
|
1504
|
-
- `error.msg`: Error message
|
|
1505
|
-
- `error.details`: Additional error details
|
|
1506
|
-
|
|
1507
|
-
- Status codes to handle:
|
|
1508
|
-
|
|
1509
|
-
If `error.code === 401`, it means your access token has expired. Update tokens immediately:
|
|
1104
|
+
```ts
|
|
1105
|
+
import {
|
|
1106
|
+
getEkaScribeInstance,
|
|
1107
|
+
type EkaScribeConfig,
|
|
1108
|
+
type RecordingOptions,
|
|
1109
|
+
} from '@eka-care/ekascribe-ts-sdk';
|
|
1510
1110
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
}
|
|
1520
|
-
});
|
|
1521
|
-
```
|
|
1111
|
+
// 1. Initialize
|
|
1112
|
+
const config: EkaScribeConfig = {
|
|
1113
|
+
access_token: token,
|
|
1114
|
+
env: 'PROD',
|
|
1115
|
+
allianceConfig: {
|
|
1116
|
+
baseUrl: 'https://api.eka.care/voice/v1',
|
|
1117
|
+
},
|
|
1118
|
+
};
|
|
1522
1119
|
|
|
1523
|
-
|
|
1120
|
+
const ekascribe = getEkaScribeInstance(config);
|
|
1524
1121
|
|
|
1525
|
-
|
|
1122
|
+
// 2. Register callbacks
|
|
1123
|
+
ekascribe.registerCallback('onTokenRequired', async () => {
|
|
1124
|
+
return await refreshToken();
|
|
1125
|
+
});
|
|
1526
1126
|
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
console.log('User started speaking');
|
|
1531
|
-
} else {
|
|
1532
|
-
console.log('User stopped speaking');
|
|
1127
|
+
ekascribe.registerCallback('onUploadEvent', (event) => {
|
|
1128
|
+
if (event.type === 'progress') {
|
|
1129
|
+
updateProgressUI(event.data.successCount, event.data.totalCount);
|
|
1533
1130
|
}
|
|
1534
1131
|
});
|
|
1535
|
-
```
|
|
1536
|
-
|
|
1537
|
-
### 3. Partial result callback
|
|
1538
|
-
|
|
1539
|
-
This callback provides real-time partial results while polling for the final output. Use it to display intermediate transcription and template results to users before processing is complete.
|
|
1540
1132
|
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
console.log('Partial result received:', partialData);
|
|
1544
|
-
|
|
1545
|
-
// Handle different poll statuses
|
|
1546
|
-
switch (partialData.poll_status) {
|
|
1547
|
-
case 'in-progress':
|
|
1548
|
-
// Processing is still ongoing, display partial results
|
|
1549
|
-
console.log('Processing...', partialData.response);
|
|
1550
|
-
break;
|
|
1551
|
-
case 'success':
|
|
1552
|
-
// Final result received
|
|
1553
|
-
console.log('Processing complete:', partialData.response);
|
|
1554
|
-
break;
|
|
1555
|
-
case 'failed':
|
|
1556
|
-
// Processing failed
|
|
1557
|
-
console.error('Processing failed:', partialData.message);
|
|
1558
|
-
break;
|
|
1559
|
-
case 'timeout':
|
|
1560
|
-
// Polling timed out
|
|
1561
|
-
console.warn('Polling timeout:', partialData.message);
|
|
1562
|
-
break;
|
|
1563
|
-
}
|
|
1133
|
+
ekascribe.registerCallback('onError', (event) => {
|
|
1134
|
+
showErrorToast(event.error.message);
|
|
1564
1135
|
});
|
|
1565
|
-
```
|
|
1566
1136
|
|
|
1567
|
-
|
|
1137
|
+
// 3. Start recording
|
|
1138
|
+
const startResult = await ekascribe.startRecordingV2({
|
|
1139
|
+
templates: ['soap'],
|
|
1140
|
+
sessionMode: 'consultation',
|
|
1141
|
+
languageHint: ['en'],
|
|
1142
|
+
patientDetails: { name: 'John Doe', age: '45', gender: 'male' },
|
|
1143
|
+
});
|
|
1568
1144
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
response: TGetStatusApiResponse | null; // The response structure is the same as returned by `pollSessionOutput` method
|
|
1573
|
-
status_code: number; // HTTP status code
|
|
1574
|
-
message: string; // Status message
|
|
1575
|
-
poll_status: 'in-progress' | 'success' | 'failed' | 'timeout'; // Current polling state
|
|
1145
|
+
if (startResult.error_code) {
|
|
1146
|
+
showError(startResult.message);
|
|
1147
|
+
return;
|
|
1576
1148
|
}
|
|
1577
|
-
```
|
|
1578
|
-
|
|
1579
|
-
**When to use:**
|
|
1580
1149
|
|
|
1581
|
-
|
|
1582
|
-
- Display partial transcription results to improve user experience
|
|
1583
|
-
- Show processing progress indicators
|
|
1584
|
-
- Handle intermediate template results
|
|
1150
|
+
const sessionId = startResult.txn_id!;
|
|
1585
1151
|
|
|
1586
|
-
|
|
1152
|
+
// 4. User interacts...
|
|
1153
|
+
// pauseBtn.onclick = () => ekascribe.pauseRecording();
|
|
1154
|
+
// resumeBtn.onclick = () => ekascribe.resumeRecording();
|
|
1587
1155
|
|
|
1588
|
-
|
|
1156
|
+
// 5. End recording
|
|
1157
|
+
const endResult = await ekascribe.endRecording();
|
|
1589
1158
|
|
|
1590
|
-
|
|
1159
|
+
if (endResult.error_code === 'audio_upload_failed') {
|
|
1160
|
+
await ekascribe.retryUploadRecording();
|
|
1161
|
+
}
|
|
1591
1162
|
|
|
1592
|
-
|
|
1593
|
-
ekascribe.
|
|
1594
|
-
|
|
1163
|
+
// 6. Get results
|
|
1164
|
+
const status = await ekascribe.getSessionStatus(sessionId, {
|
|
1165
|
+
poll: {
|
|
1166
|
+
maxAttempts: 60,
|
|
1167
|
+
intervalMs: 2000,
|
|
1168
|
+
onProgress: (s) => updateStatusUI(s.status),
|
|
1169
|
+
},
|
|
1595
1170
|
});
|
|
1596
|
-
```
|
|
1597
|
-
|
|
1598
|
-
### 5. VAD frame processed callback
|
|
1599
1171
|
|
|
1600
|
-
|
|
1172
|
+
if (status.success) {
|
|
1173
|
+
displayResults(status.data.templates, status.data.transcript);
|
|
1174
|
+
}
|
|
1601
1175
|
|
|
1602
|
-
|
|
1603
|
-
ekascribe.
|
|
1604
|
-
console.log('VAD frame processed:', processedData);
|
|
1605
|
-
});
|
|
1176
|
+
// 7. Cleanup on unmount
|
|
1177
|
+
await ekascribe.resetInstance();
|
|
1606
1178
|
```
|
|
1607
1179
|
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
| Error Code | Description |
|
|
1611
|
-
| --------------------- | ----------------------------------------------------------- |
|
|
1612
|
-
| `microphone` | Microphone access error (permission denied or unavailable) |
|
|
1613
|
-
| `txn_init_failed` | Failed to initialize transaction |
|
|
1614
|
-
| `txn_limit_exceeded` | Maximum number of concurrent transactions exceeded |
|
|
1615
|
-
| `unknown_error` | An unknown or unclassified error occurred |
|
|
1616
|
-
| `txn_stop_failed` | Error occurred while stopping the transaction |
|
|
1617
|
-
| `audio_upload_failed` | Audio file upload to server failed |
|
|
1618
|
-
| `txn_commit_failed` | Commit call failed for the current transaction |
|
|
1619
|
-
| `invalid_request` | Request to SDK was malformed or missing required parameters |
|
|
1620
|
-
| `vad_not_initialized` | Voice activity detection engine was not initialized |
|
|
1621
|
-
| `no_audio_capture` | No audio was captured during the recording session |
|
|
1622
|
-
| `txn_status_mismatch` | Invalid operation due to mismatched transaction status |
|
|
1180
|
+
---
|
|
1623
1181
|
|
|
1624
1182
|
## Contribution Guidelines
|
|
1625
1183
|
|
|
1626
|
-
This is a continually updated, open source project.
|
|
1627
|
-
Contributions are welcome!
|
|
1628
|
-
|
|
1629
|
-
## Tips
|
|
1630
|
-
|
|
1631
|
-
- The SDK internally handles shared worker logic to reduce load on the main thread. Try to execute these functions in the main thread to avoid unnecessary issues.
|
|
1632
|
-
|
|
1633
|
-
## Advanced Usage (for later use)
|
|
1634
|
-
|
|
1635
|
-
- Maximum retries for file upload in case of failure.
|
|
1636
|
-
- Update VAD configurations
|
|
1637
|
-
|
|
1638
|
-
## Under Development
|
|
1639
|
-
|
|
1640
|
-
- Opus compression of audio files
|
|
1641
|
-
- Test cases
|
|
1184
|
+
This is a continually updated, open source project. Contributions are welcome!
|
|
1642
1185
|
|
|
1643
|
-
Refer [
|
|
1186
|
+
Refer [EkaScribe TS SDK](https://github.com/eka-care/eka-js-sdk) for SDK implementation.
|