@scribeberry/sdk 0.2.0
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/LICENSE +22 -0
- package/README.md +312 -0
- package/dist/index.d.mts +707 -0
- package/dist/index.d.ts +707 -0
- package/dist/index.js +832 -0
- package/dist/index.mjs +791 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Scribeberry Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# @scribeberry/sdk
|
|
2
|
+
|
|
3
|
+
Official Scribeberry SDK for Node.js and browsers. AI-powered medical
|
|
4
|
+
transcription, note generation, and realtime speech-to-text.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @scribeberry/sdk
|
|
10
|
+
# or
|
|
11
|
+
pnpm add @scribeberry/sdk
|
|
12
|
+
# or
|
|
13
|
+
yarn add @scribeberry/sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick Start — Server-Side (Node.js)
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { Scribeberry } from '@scribeberry/sdk';
|
|
20
|
+
|
|
21
|
+
const sb = new Scribeberry({
|
|
22
|
+
apiKey: 'sk_live_...',
|
|
23
|
+
baseUrl: 'https://sandbox.api.scribeberry.com', // omit for production
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// List templates
|
|
27
|
+
const { items } = await sb.templates.list();
|
|
28
|
+
console.log(items.map((t) => t.name));
|
|
29
|
+
|
|
30
|
+
// Generate a note from text
|
|
31
|
+
const result = await sb.notes.generate({
|
|
32
|
+
templateId: items[0].id,
|
|
33
|
+
conversationText: 'Patient presents with chest pain...',
|
|
34
|
+
});
|
|
35
|
+
console.log(result.note.markdown);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start — Browser (Realtime Transcription)
|
|
39
|
+
|
|
40
|
+
Realtime transcription in the browser uses **temporary tokens** so your API key
|
|
41
|
+
is never exposed client-side. The SDK manages token lifecycle automatically.
|
|
42
|
+
|
|
43
|
+
### Step 1: Server creates a token endpoint
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// server.ts (Node.js)
|
|
47
|
+
import { Scribeberry } from '@scribeberry/sdk';
|
|
48
|
+
|
|
49
|
+
const sb = new Scribeberry({ apiKey: 'sk_live_...' });
|
|
50
|
+
|
|
51
|
+
app.post('/api/realtime-token', async (req, res) => {
|
|
52
|
+
const { token, expiresAt } = await sb.realtime.createToken({
|
|
53
|
+
expiresInSeconds: 3600, // 1 hour max
|
|
54
|
+
});
|
|
55
|
+
res.json({ token, expiresAt });
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Step 2: Browser uses a token callback
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// client.ts (browser)
|
|
63
|
+
import { Scribeberry } from '@scribeberry/sdk';
|
|
64
|
+
|
|
65
|
+
// Provide a callback — SDK fetches tokens automatically and refreshes before expiry
|
|
66
|
+
const sb = new Scribeberry({
|
|
67
|
+
getRealtimeToken: async () => {
|
|
68
|
+
const res = await fetch('/api/realtime-token', { method: 'POST' });
|
|
69
|
+
return res.json(); // { token, expiresAt }
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Start a realtime transcription session
|
|
74
|
+
const session = sb.realtime.transcribe({
|
|
75
|
+
language: 'en-US',
|
|
76
|
+
enableDiarization: true,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Listen for events
|
|
80
|
+
session.on('partial', (text) => {
|
|
81
|
+
// Interim text (updates rapidly as you speak)
|
|
82
|
+
document.getElementById('interim').textContent = text;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
session.on('final', (segment) => {
|
|
86
|
+
// Confirmed text (stable, won't change)
|
|
87
|
+
document.getElementById('transcript').textContent += segment.text + ' ';
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
session.on('error', (err) => console.error(err));
|
|
91
|
+
|
|
92
|
+
// Stream audio from microphone
|
|
93
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
94
|
+
const audioCtx = new AudioContext({ sampleRate: 16000 });
|
|
95
|
+
const source = audioCtx.createMediaStreamSource(stream);
|
|
96
|
+
const processor = audioCtx.createScriptProcessor(4096, 1, 1);
|
|
97
|
+
|
|
98
|
+
processor.onaudioprocess = (e) => {
|
|
99
|
+
const float32 = e.inputBuffer.getChannelData(0);
|
|
100
|
+
const int16 = new Int16Array(float32.length);
|
|
101
|
+
for (let i = 0; i < float32.length; i++) {
|
|
102
|
+
int16[i] = Math.max(
|
|
103
|
+
-32768,
|
|
104
|
+
Math.min(32767, Math.round(float32[i] * 32767)),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
session.sendAudio(int16.buffer);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
source.connect(processor);
|
|
111
|
+
processor.connect(audioCtx.destination);
|
|
112
|
+
|
|
113
|
+
// When done
|
|
114
|
+
const result = await session.stop();
|
|
115
|
+
console.log(result.transcript);
|
|
116
|
+
console.log(`Duration: ${result.durationSeconds}s`);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API Reference
|
|
120
|
+
|
|
121
|
+
### `new Scribeberry(config)`
|
|
122
|
+
|
|
123
|
+
| Parameter | Type | Required | Description |
|
|
124
|
+
| ------------------------- | ---------- | -------- | ----------------------------------------------------------------- |
|
|
125
|
+
| `config.apiKey` | `string` | \* | API key (`sk_test_*`/`sk_live_*`) or temp token (`sb_rt_*`) |
|
|
126
|
+
| `config.getRealtimeToken` | `function` | \* | Async callback returning `{ token, expiresAt }` — for browser use |
|
|
127
|
+
| `config.baseUrl` | `string` | | API URL. Default: `https://api.scribeberry.com` |
|
|
128
|
+
| `config.timeout` | `number` | | Request timeout in ms. Default: `30000` |
|
|
129
|
+
|
|
130
|
+
\* Provide either `apiKey` or `getRealtimeToken` (or both). For browser
|
|
131
|
+
realtime, `getRealtimeToken` is recommended.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### Templates
|
|
136
|
+
|
|
137
|
+
#### `sb.templates.list(options?)`
|
|
138
|
+
|
|
139
|
+
List all templates (paginated).
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
const { items, meta } = await sb.templates.list({ page: 1, pageSize: 20 });
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
| Option | Type | Description |
|
|
146
|
+
| ----------- | ----------------- | ----------------------- |
|
|
147
|
+
| `page` | `number` | Page number (1-indexed) |
|
|
148
|
+
| `pageSize` | `number` | Items per page |
|
|
149
|
+
| `sortBy` | `string` | Field to sort by |
|
|
150
|
+
| `sortOrder` | `'asc' \| 'desc'` | Sort direction |
|
|
151
|
+
|
|
152
|
+
#### `sb.templates.get(id)`
|
|
153
|
+
|
|
154
|
+
Get a single template by ID.
|
|
155
|
+
|
|
156
|
+
#### `sb.templates.create(options)`
|
|
157
|
+
|
|
158
|
+
Create a new template.
|
|
159
|
+
|
|
160
|
+
#### `sb.templates.delete(id)`
|
|
161
|
+
|
|
162
|
+
Delete a template.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
### Notes
|
|
167
|
+
|
|
168
|
+
#### `sb.notes.generate(options)`
|
|
169
|
+
|
|
170
|
+
Generate a medical note from text or audio.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const result = await sb.notes.generate({
|
|
174
|
+
templateId: 'template-id',
|
|
175
|
+
conversationText: 'Patient presents with...',
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
console.log(result.note.markdown);
|
|
179
|
+
console.log(result.note.structured); // { "Chief Complaint": "...", ... }
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
| Option | Type | Required | Description |
|
|
183
|
+
| ---------------------- | ----------------------------- | -------- | -------------------------------- |
|
|
184
|
+
| `templateId` | `string` | ✅ | Template to use |
|
|
185
|
+
| `conversationText` | `string` | | Text to generate from |
|
|
186
|
+
| `audioUrl` | `string` | | Audio URL to transcribe first |
|
|
187
|
+
| `sourceLanguage` | `string` | | Language code (default: `en-US`) |
|
|
188
|
+
| `transcriptionQuality` | `'low' \| 'medium' \| 'high'` | | Quality (default: `high`) |
|
|
189
|
+
| `context` | `Record<string, string>` | | Additional context |
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
### Realtime Transcription
|
|
194
|
+
|
|
195
|
+
#### `sb.realtime.createToken(options?)` _(server-side only)_
|
|
196
|
+
|
|
197
|
+
Create a temporary token for browser-side WebSocket access.
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const { token, wsUrl, expiresAt } = await sb.realtime.createToken({
|
|
201
|
+
expiresInSeconds: 3600,
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
| Option | Type | Description |
|
|
206
|
+
| ------------------ | -------- | ----------------------------------------- |
|
|
207
|
+
| `expiresInSeconds` | `number` | Token lifetime (default: 3600, max: 3600) |
|
|
208
|
+
|
|
209
|
+
**Returns:** `{ token: string, wsUrl: string, expiresAt: string }`
|
|
210
|
+
|
|
211
|
+
#### `sb.realtime.transcribe(config?)`
|
|
212
|
+
|
|
213
|
+
Start a realtime transcription session via WebSocket.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const session = sb.realtime.transcribe({
|
|
217
|
+
language: 'en-US',
|
|
218
|
+
enableDiarization: true,
|
|
219
|
+
templateId: 'template-id', // optional: auto-generate note on stop
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
| Option | Type | Description |
|
|
224
|
+
| ------------------- | --------- | ---------------------------------------- |
|
|
225
|
+
| `language` | `string` | Language code (default: `en-US`) |
|
|
226
|
+
| `enableDiarization` | `boolean` | Speaker identification (default: `true`) |
|
|
227
|
+
| `templateId` | `string` | Auto-generate note on stop |
|
|
228
|
+
|
|
229
|
+
#### Session Events
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
session.on('partial', (text: string, speaker?: number) => {});
|
|
233
|
+
session.on('final', (segment: TranscriptSegment) => {});
|
|
234
|
+
session.on('endpoint', () => {});
|
|
235
|
+
session.on('started', (sessionId: string) => {});
|
|
236
|
+
session.on('stopped', (result: RealtimeSessionResult) => {});
|
|
237
|
+
session.on('note', (note: Note) => {});
|
|
238
|
+
session.on('error', (error: Error) => {});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
| Event | Description |
|
|
242
|
+
| ---------- | ------------------------------------------------------- |
|
|
243
|
+
| `partial` | Interim transcript (updates rapidly, replaces previous) |
|
|
244
|
+
| `final` | Confirmed segment (stable text, accumulate these) |
|
|
245
|
+
| `endpoint` | Natural speech pause detected |
|
|
246
|
+
| `started` | Session ready to receive audio |
|
|
247
|
+
| `stopped` | Session ended with final results |
|
|
248
|
+
| `note` | Generated note (if `templateId` was set) |
|
|
249
|
+
| `error` | An error occurred |
|
|
250
|
+
|
|
251
|
+
#### Session Methods
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
session.sendAudio(data); // Send PCM audio chunk
|
|
255
|
+
session.sendStream(asyncIterable); // Stream from async source
|
|
256
|
+
session.pause(); // Pause (connection stays alive)
|
|
257
|
+
session.resume(); // Resume streaming
|
|
258
|
+
session.finalize(); // Flush pending audio
|
|
259
|
+
session.getTranscript(); // Full transcript text so far
|
|
260
|
+
session.getSegments(); // All confirmed segments
|
|
261
|
+
const result = await session.stop(); // Stop and get final result
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Audio Format Requirements
|
|
267
|
+
|
|
268
|
+
For realtime transcription, audio must be:
|
|
269
|
+
|
|
270
|
+
- **Format:** PCM 16-bit signed little-endian (`pcm_s16le`)
|
|
271
|
+
- **Sample rate:** 16,000 Hz
|
|
272
|
+
- **Channels:** 1 (mono)
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Error Handling
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import {
|
|
280
|
+
AuthenticationError,
|
|
281
|
+
RateLimitError,
|
|
282
|
+
Scribeberry,
|
|
283
|
+
ScribeberryError,
|
|
284
|
+
} from '@scribeberry/sdk';
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
await sb.templates.list();
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (error instanceof AuthenticationError) {
|
|
290
|
+
// Invalid or expired API key / token
|
|
291
|
+
} else if (error instanceof RateLimitError) {
|
|
292
|
+
// Too many requests
|
|
293
|
+
} else if (error instanceof ScribeberryError) {
|
|
294
|
+
console.error(`[${error.code}] ${error.message}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Environments
|
|
302
|
+
|
|
303
|
+
| Environment | Base URL | Key Prefix |
|
|
304
|
+
| ----------- | ------------------------------------- | ----------- |
|
|
305
|
+
| Production | `https://api.scribeberry.com` | `sk_live_*` |
|
|
306
|
+
| Sandbox | `https://sandbox.api.scribeberry.com` | `sk_test_*` |
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## License
|
|
311
|
+
|
|
312
|
+
MIT © [Scribeberry](https://scribeberry.com)
|