@vocalstack/js-sdk 1.1.0 → 2.0.0-alpha.1

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.
Files changed (73) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +112 -65
  3. package/SKILL.md +249 -0
  4. package/dist/errors.d.ts +22 -0
  5. package/dist/errors.js +56 -0
  6. package/dist/file.d.ts +2 -0
  7. package/dist/file.js +38 -0
  8. package/dist/http.d.ts +43 -0
  9. package/dist/http.js +124 -0
  10. package/dist/index.d.ts +42 -2
  11. package/dist/index.js +51 -17
  12. package/dist/mic.d.ts +27 -0
  13. package/dist/mic.js +236 -0
  14. package/dist/realtime.d.ts +77 -0
  15. package/dist/realtime.js +490 -0
  16. package/dist/resources.d.ts +374 -0
  17. package/dist/resources.js +712 -0
  18. package/dist/subscription.d.ts +38 -0
  19. package/dist/subscription.js +153 -0
  20. package/dist/ws.d.ts +9 -0
  21. package/dist/ws.js +12 -0
  22. package/package.json +32 -36
  23. package/skills/vocalstack/SKILL.md +249 -0
  24. package/dist/ApiVersion.d.ts +0 -2
  25. package/dist/ApiVersion.js +0 -32
  26. package/dist/RestApi.d.ts +0 -21
  27. package/dist/RestApi.js +0 -95
  28. package/dist/VocalStackApi.d.ts +0 -19
  29. package/dist/VocalStackApi.js +0 -60
  30. package/dist/WebsocketApi.d.ts +0 -38
  31. package/dist/WebsocketApi.js +0 -150
  32. package/dist/apis/Billing.d.ts +0 -59
  33. package/dist/apis/Billing.js +0 -21
  34. package/dist/apis/LiveTranscription.d.ts +0 -58
  35. package/dist/apis/LiveTranscription.js +0 -55
  36. package/dist/apis/Polyglot.d.ts +0 -113
  37. package/dist/apis/Polyglot.js +0 -68
  38. package/dist/apis/Security.d.ts +0 -25
  39. package/dist/apis/Security.js +0 -35
  40. package/dist/apis/Transcriptions.d.ts +0 -60
  41. package/dist/apis/Transcriptions.js +0 -38
  42. package/dist/apis/UrlTranscription.d.ts +0 -59
  43. package/dist/apis/UrlTranscription.js +0 -53
  44. package/dist/apis/index.d.ts +0 -6
  45. package/dist/apis/index.js +0 -22
  46. package/dist/esm/ApiVersion.d.ts +0 -2
  47. package/dist/esm/ApiVersion.js +0 -28
  48. package/dist/esm/RestApi.d.ts +0 -21
  49. package/dist/esm/RestApi.js +0 -91
  50. package/dist/esm/VocalStackApi.d.ts +0 -19
  51. package/dist/esm/VocalStackApi.js +0 -56
  52. package/dist/esm/WebsocketApi.d.ts +0 -38
  53. package/dist/esm/WebsocketApi.js +0 -146
  54. package/dist/esm/apis/Billing.d.ts +0 -59
  55. package/dist/esm/apis/Billing.js +0 -17
  56. package/dist/esm/apis/LiveTranscription.d.ts +0 -58
  57. package/dist/esm/apis/LiveTranscription.js +0 -51
  58. package/dist/esm/apis/Polyglot.d.ts +0 -113
  59. package/dist/esm/apis/Polyglot.js +0 -64
  60. package/dist/esm/apis/Security.d.ts +0 -25
  61. package/dist/esm/apis/Security.js +0 -31
  62. package/dist/esm/apis/Transcriptions.d.ts +0 -60
  63. package/dist/esm/apis/Transcriptions.js +0 -34
  64. package/dist/esm/apis/UrlTranscription.d.ts +0 -59
  65. package/dist/esm/apis/UrlTranscription.js +0 -49
  66. package/dist/esm/apis/index.d.ts +0 -6
  67. package/dist/esm/apis/index.js +0 -6
  68. package/dist/esm/generics.zod.d.ts +0 -83
  69. package/dist/esm/generics.zod.js +0 -47
  70. package/dist/esm/index.d.ts +0 -2
  71. package/dist/esm/index.js +0 -2
  72. package/dist/generics.zod.d.ts +0 -83
  73. package/dist/generics.zod.js +0 -50
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- VocalStack Proprietary License
2
-
3
- Copyright (C) 2024 VocalStack
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software solely in connection with the VocalStack service, subject to the following conditions:
6
-
7
- ## Usage Limitations
8
-
9
- 1. **Compliance with Terms of Service**: The Software must be used in a manner that does not violate the VocalStack terms of service ([https://www.vocalstack.com/terms-of-service](https://www.vocalstack.com/terms-of-service)).
10
-
11
- 2. **Commercial Use Allowed**: The Licensed Work may be used for commercial purposes, provided the user has a valid API key and an active subscription to the VocalStack service. Unauthorized use is strictly prohibited.
12
-
13
- 3. **Modification and Contributions**: You may view and modify the Licensed Work for internal use only. Contributions to the Licensed Work may be made through pull requests. By submitting a contribution, you agree to assign the rights of the contribution to VocalStack.
14
-
15
- 4. **Redistribution Allowed**: Redistribution of the Software, in whole or in part, whether modified or unmodified, is permitted, provided that it does not violate the company's terms of service.
16
-
17
- 5. **Termination**: VocalStack reserves the right to terminate your access to and use of the Software at any time if you fail to comply with these terms.
18
-
19
- ## Disclaimer
20
-
21
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VocalStack
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.
package/README.md CHANGED
@@ -1,89 +1,136 @@
1
- [![](/logo.svg 'VocalStack')](https://www.vocalstack.com)
1
+ # @vocalstack/js-sdk
2
2
 
3
- > VocalStack is a platform that can be used for building voice applications. See more at [https://www.vocalstack.com](vocalstack.com).
3
+ The VocalStack speech API for Node (20 or newer) and browsers: one client,
4
+ `VocalStack`, with one group per resource, typed from the API's one OpenAPI
5
+ document (`/v1/openapi.json`, generated into `@vocalstack/contract`).
4
6
 
5
- This repository contains the official VocalStack SDK for JavaScript. It provides a simple way
6
- to interact with the [VocalStack API](https://www.vocalstack.com/api).
7
- Most notably, it helps implement asynchronous bidirectional communication
8
- such as live stream transcriptions in just a few lines of code.
9
- This SDK will work in both Node.js and the browser.
7
+ Read more about the [VocalStack JavaScript SDK](https://vocalstack.com/#developers) and our other SDKs on the [VocalStack website](https://vocalstack.com/).
10
8
 
9
+ ```ts
10
+ import { VocalStack } from '@vocalstack/js-sdk';
11
11
 
12
- # Installation
12
+ const vs = new VocalStack({ apiKey: process.env.VOCALSTACK_API_KEY }); // baseUrl defaults to https://api.vocalstack.com
13
13
 
14
- Before you start, install the VocalStack JavaScript SDK using your preferred Node package manager, such as npm:
14
+ // A stored transcription of a link (or `file:` a path, Blob or bytes).
15
+ const tx = await vs.transcriptions.create({ url: 'https://example.com/talk.mp3' });
16
+ const job = await vs.jobs.wait(tx.job!.recordingId); // done | failed | cropping
17
+ const { sentences } = await vs.recordings.transcript(job.recordingId);
15
18
 
16
- ```bash
17
- npm install @vocalstack/js-sdk
19
+ const srt = await (await vs.recordings.export(job.recordingId, 'srt')).text();
18
20
  ```
19
21
 
20
- Also make sure to have a [VocalStack account](https://www.vocalstack.com) and an API key, which you can generate
21
- at https://www.vocalstack.com/dashboard/api-keys.
22
- There is a free tier available, so you can start experimenting right away.
23
-
22
+ <!-- sdk-docs: generated from the VocalStack API guides; edits here are overwritten -->
24
23
 
25
- # Quick Example
24
+ ## Install
26
25
 
27
- Here is a quick example of how to use the SDK to transcribe a file from a URL:
28
- ```javascript
29
- import { UrlTranscription } from '@vocalstack/js-sdk';
26
+ ```sh
27
+ npm install @vocalstack/js-sdk
28
+ ```
30
29
 
31
- const sdk = new UrlTranscription({ apiKey: 'YOUR-API-KEY' });
32
- const transcription = await sdk.connect({
33
- url: 'http://example.com/files/meaningless.mp3',
30
+ ## Guides
31
+
32
+ - [Quickstart](https://github.com/vocalstack/js-sdk/blob/main/docs/quickstart.md): An API key, the SDK, and your first transcript in a dozen lines.
33
+ - [Transcribe a file](https://github.com/vocalstack/js-sdk/blob/main/docs/transcribe-a-file.md): Upload a file, wait for its job, read the transcript and export it. Asynchronous; nothing to keep open.
34
+ - [Transcribe a link](https://github.com/vocalstack/js-sdk/blob/main/docs/transcribe-a-link.md): The server fetches a public URL or a YouTube video, then transcribes it like a file. Asynchronous, with a fetch phase first.
35
+ - [Transcribe a batch of files](https://github.com/vocalstack/js-sdk/blob/main/docs/transcribe-a-batch.md): Send many files or links as one batch, start it once, and read every transcript when it is done. Cheaper than one transcription per file.
36
+ - [Stream a microphone](https://github.com/vocalstack/js-sdk/blob/main/docs/stream-a-microphone.md): Live audio in, transcript events out, over one WebSocket. Drafts replace the live tail; commits append.
37
+ - [Stream a file at full speed](https://github.com/vocalstack/js-sdk/blob/main/docs/stream-a-file.md): Send a file's bytes through a realtime session and read the transcript as fast as the server makes it. Nothing is stored.
38
+ - [Go live](https://github.com/vocalstack/js-sdk/blob/main/docs/go-live.md): Publish live audio to a stored transcription, and put it on a broadcast link listeners open. Control is HTTP; audio is one WebSocket.
39
+ - [Listen](https://github.com/vocalstack/js-sdk/blob/main/docs/listen.md): Follow a recording as it happens, in any language: subscribe, catch up from a snapshot, then apply live frames in order.
40
+ - [Live translation and spoken translations](https://github.com/vocalstack/js-sdk/blob/main/docs/translation-and-voices.md): Translate a stream as it is spoken, and have each translated sentence spoken back in a voice. Text frames and audio clips, over the same socket.
41
+ - [Connect an app with one click](https://github.com/vocalstack/js-sdk/blob/main/docs/connect-an-app.md): Let the people who use your app connect their VocalStack account with one Authenticate click — OAuth 2.0 authorization code with PKCE, where the access token is an API key — instead of pasting a key.
42
+ - [Errors, retries and limits](https://github.com/vocalstack/js-sdk/blob/main/docs/errors-and-limits.md): One error envelope everywhere; what to retry (429, 503 unavailable) and how to make a retry safe (Idempotency-Key); credit, plan limits and the 402s.
43
+
44
+ <!-- /sdk-docs -->
45
+
46
+ ## What is what
47
+
48
+ | Name | What it is | Group |
49
+ |---|---|---|
50
+ | realtime session | a signed token plus one socket; nothing stored | `vs.realtime` |
51
+ | broadcast | the link listeners open | `vs.broadcasts` |
52
+ | transcription | a file, link, microphone or stream, transcribed and stored (`live: true` for a microphone or stream) | `vs.transcriptions` |
53
+ | recording | one take inside a broadcast or a transcription | `vs.recordings` |
54
+ | job | a file or link recording's transcription work | `vs.jobs` |
55
+ | batch | many files transcribed together, for less than one transcription each | `vs.batches` |
56
+
57
+ The other groups: `vs.corrections`, `vs.shareLinks`, `vs.voices`,
58
+ `vs.search`, `vs.me`, `vs.languages`, `vs.rtmp`, `vs.events`, `vs.meta`.
59
+ Method names follow the document's `operationId`s (`broadcasts.startRecording`
60
+ is `POST /v1/broadcasts/{id}/recordings`); lists return arrays.
61
+
62
+ ## Options
63
+
64
+ ```ts
65
+ new VocalStack({
66
+ baseUrl, // default https://api.vocalstack.com
67
+ apiKey, // an API key (vs_key_…), for a server
68
+ token, // or, in a browser: an access token / browser token, or a getter (read per request)
69
+ actingPrincipal, // act for an organization (X-Acting-Principal)
70
+ deviceId, // correlation only (X-Device-Id)
71
+ fetch, // any fetch
72
+ maxRetries: 2, // 429s, and 503 unavailable on safe calls, retried after Retry-After (at most 60 s each)
73
+ readyDeadlineMs, // how long a realtime socket may take to report ready
34
74
  });
35
-
36
- transcription.start();
37
-
38
- // This will print the transcription data as it comes in
39
- transcription.onData(console.log);
40
-
41
- /*
42
- {
43
- status: 'processing',
44
- data: {
45
- id: 'd1e7b3b0-7b3b-4b3b-8b3b-0b3b7b3b3b3b',
46
- operation: 'transcription-prerecorded',
47
- progress: 0.1,
48
- timeline: [
49
- {
50
- start: 0,
51
- end: 7.52,
52
- text: 'Meaningless, meaningless, says the teacher, utterly meaningless, everything is meaningless.',
53
- language: 'en',
54
- translations: { ... },
55
- },
56
- ...
57
- ]
58
- }
59
- }
60
- */
61
75
  ```
62
76
 
77
+ Every call that creates something takes `{ idempotencyKey }` last.
63
78
 
64
- # Documentation Index
79
+ ## Realtime sessions
65
80
 
66
- - [Get Transcription Data](https://github.com/vocalstack/js-sdk/blob/main/documentation/get-transcription-data.md):
67
- Get data from pending or completed transcriptions. This includes the transcription timeline, key words, summary, and paragraph segments.
81
+ ```ts
82
+ const session = await vs.realtime.connect({ spokenLanguage: 'en' });
83
+ session.sendAudio(pcmChunk); // raw PCM, f32le, 16 kHz, mono, as binary frames
84
+ for await (const event of session) {
85
+ if (event.type === 'transcript.committed') render(event.segment);
86
+ }
87
+ session.eof(); // at end of source: final commits, then session.done
68
88
 
69
- - [Transcribe from a Microphone or LiveStream](https://github.com/vocalstack/js-sdk/blob/main/documentation/transcribe-from-a-microphone-or-live-stream.md):
70
- Transcribe live speech from a microphone or live stream. Integrate with Polyglot to create a public shareable link for the transcription which users can read in any language.
89
+ // A whole file through one socket, nothing stored:
90
+ const { text, segments } = await vs.realtime.transcribeFile('meeting.mp3', { spokenLanguage: 'en' });
91
+ ```
71
92
 
72
- - [Transcription Sessions](https://github.com/vocalstack/js-sdk/blob/main/documentation/transcription-sessions.md):
73
- Monitor and manage transcription state with sessions. Using sessions you can reconnect to a previously created asynchronous connection.
93
+ - `transcript.draft` frames **fully replace** the live tail; never append them.
94
+ - `transcript.committed` segments are immutable, gapless by `segment.seq`,
95
+ and carry word timings.
96
+ - A dropped connection ends the session (no resume): start a new one.
97
+
98
+ ## Live recordings and listeners
99
+
100
+ ```ts
101
+ const b = await vs.broadcasts.create({ name: 'Release standup' }); // b.code is the link
102
+ const grant = await vs.broadcasts.startRecording(b.id); // 409 conflict + details.takeover
103
+ // when another device records
104
+ const stream = await vs.recordings.publish(grant); // the publisher socket
105
+ await vs.recordings.pause(grant.recording.id);
106
+ await vs.recordings.resume(grant.recording.id);
107
+ await vs.recordings.stop(grant.recording.id); // only from paused
108
+
109
+ // A listener (no account), holding a share link's code:
110
+ const link = await vs.broadcasts.createShareLink(b.id, { label: 'Audience' });
111
+ const anon = new VocalStack({ baseUrl, deviceId });
112
+ const sub = await anon.recordings.subscribe(grant.recording.id, { lang: 'es', shareCode: link.code, asOwner: false });
113
+ for await (const ev of sub) {
114
+ if (ev.type === 'snapshot') render(ev.snapshot); // backfill, initial or after a gap
115
+ if (ev.type === 'frame') apply(ev.frame); // transcript.*, recording.state, ...
116
+ }
117
+ ```
74
118
 
75
- - [Translate a Transcription](https://github.com/vocalstack/js-sdk/blob/main/documentation/translate-transcription-to-another-language.md):
76
- Translate transcribed text to another language. This can be done for any transcription, including prerecorded transcriptions, live transcriptions or Polyglot session transcriptions.
119
+ Browser microphone capture ships as `@vocalstack/js-sdk/mic`
120
+ (`streamMicrophone(session, { onStall, onRecover })`).
77
121
 
78
- - [Transcribe Audio from URL](https://github.com/vocalstack/js-sdk/blob/main/documentation/transcribe-audio-from-url-to-text.md):
79
- Transcribe speech from pre-recorded audio in a URL to plain text. Major file formats are supported, including MP3, WAV, FLAC, and OGG.
122
+ ## Errors
80
123
 
81
- - [Client Side Authentication Tokens](https://github.com/vocalstack/js-sdk/blob/main/documentation/transcribe-on-the-front-end-with-auth-tokens.md):
82
- Create a temporary authentication token for client side requests. Safely implement API requests in web browsers without exposing your API keys.
124
+ `VocalStackError` carries `code` (the contract's closed enum), `message`,
125
+ `status`, `retryable`, `details` (quota numbers, code suggestions,
126
+ `{ recordingId, takeover }`), `headers` and `retryAfter`.
83
127
 
84
- - [Transcription Request and Response](https://github.com/vocalstack/js-sdk/blob/main/documentation/transcription-request-and-response.md):
85
- Common request options and responses for all transcription operations. Use options to configure the transcription settings.
128
+ ## Binary answers
86
129
 
87
- - [Transcribe and Present a Polyglot Session](https://github.com/vocalstack/js-sdk/blob/main/documentation/transcribe-and-present-a-polyglot-session.md):
88
- Create a session that can be used to broadcast a live transcription via a public shareable link. Users can read live transcriptions in their preferred language, and even past transcriptions when your session is inactive.
130
+ `recordings.export`, `recordings.clip`, `recordings.voiceClip`, `jobs.preview`,
131
+ `voices.preview`, `voices.clonePreview` and `voices.sample` return the
132
+ `Response` itself: `.text()`, `.arrayBuffer()` or stream it.
133
+ `recordings.audioUrl` returns the signed media URL.
89
134
 
135
+ Verified against a language-agnostic conformance suite shared by every
136
+ VocalStack SDK and against a running API. For agents: `SKILL.md`.
package/SKILL.md ADDED
@@ -0,0 +1,249 @@
1
+ ---
2
+ name: vocalstack
3
+ description: Use when writing Node or TypeScript code that calls the VocalStack speech API with @vocalstack/js-sdk, for example to transcribe an audio or video URL or a local file, stream live audio for realtime transcription, fetch or export a transcript (JSON, SRT, XLSX, PDF), or search transcripts. Covers installing, API keys, base URLs, five copy-paste programs and error handling (401, 402, 403, 429).
4
+ ---
5
+
6
+ # VocalStack TypeScript SDK
7
+
8
+ The VocalStack API transcribes speech (files, links, live audio), with speaker
9
+ labels and translation. `@vocalstack/js-sdk` has one client, `VocalStack`, with
10
+ one group per resource: `vs.transcriptions`, `vs.jobs`, `vs.recordings`,
11
+ `vs.broadcasts`, `vs.search`, `vs.realtime` and a few more (listed at the end).
12
+
13
+ ## What is what
14
+
15
+ - A **transcription** is a file, a link, a microphone or a stream, transcribed
16
+ and stored. Create one with `vs.transcriptions.create(...)`.
17
+ - A **recording** is one take inside it. Its id (`recordingId`) is what you
18
+ read the transcript and exports with: `vs.recordings.transcript(recordingId)`.
19
+ - A **job** is a file or link recording's transcription work:
20
+ `vs.jobs.wait(recordingId)` polls it until it is `done` or `failed`
21
+ (`{ onProgress: (job) => … }` sees each read: `job.state`, `job.progress.pct`).
22
+ - A **batch** is many files transcribed together for less than one
23
+ transcription each: `vs.batches.create()`, `addFile`/`addUrl` per file,
24
+ `start`, then `wait` (section 6).
25
+ - A **broadcast** is a link listeners open to follow a live recording.
26
+ - A **realtime session** (`vs.realtime`) is a socket you stream audio into and
27
+ read the transcript from. Nothing is stored. "Session" means only this.
28
+
29
+ ## Install
30
+
31
+ ```sh
32
+ npm init -y && npm pkg set type=module # in a new folder: the programs below are ESM
33
+ npm i @vocalstack/js-sdk # Node 20 or newer
34
+ ```
35
+
36
+ To run code newer than the npm release, install it from a clone of
37
+ https://github.com/vocalstack/js-sdk. Build it
38
+ first (`npm run build` also builds the bundled `contract/` package):
39
+
40
+ ```sh
41
+ git clone https://github.com/vocalstack/js-sdk && (cd js-sdk && npm install && npm run build)
42
+ npm i ./js-sdk
43
+ ```
44
+
45
+ Use ESM (`"type": "module"` in package.json, or `.mjs` files).
46
+
47
+ Speaker numbers start at 0. A speaker of `-1` means speaker labels were off
48
+ for that transcription, so print the text without a speaker.
49
+
50
+ ## Auth and base URL
51
+
52
+ - Read the key from `VOCALSTACK_API_KEY` and pass it as `apiKey`. It goes out
53
+ as `Authorization: Bearer <key>`, also on sockets.
54
+ - An API key starts with `vs_key_`. There is one kind of key: every call is
55
+ real usage on the account's tier and quotas.
56
+ - Keys are for servers. Never put a key in browser code or in a URL. The API
57
+ refuses a key sent from a web page (`Origin` header) or in a query string.
58
+ - Scopes: `read` (read transcripts, search, export), `write` and
59
+ `recordings:publish` (create transcriptions, stream live audio), or `*`.
60
+ - Base URL: `https://api.vocalstack.com` (the default). Override it with
61
+ `VOCALSTACK_BASE_URL` (for example, a local or staging server).
62
+
63
+ Every program below starts like this:
64
+
65
+ ```js
66
+ import { VocalStack } from '@vocalstack/js-sdk';
67
+ const vs = new VocalStack({
68
+ baseUrl: process.env.VOCALSTACK_BASE_URL, // absent: https://api.vocalstack.com
69
+ apiKey: process.env.VOCALSTACK_API_KEY,
70
+ });
71
+ ```
72
+
73
+ ## 1. Transcribe a URL and wait for the result
74
+
75
+ The server fetches the link itself, so it must be a public http(s) address.
76
+ Private, loopback and cloud-metadata addresses are refused.
77
+
78
+ ```js
79
+ import { VocalStack } from '@vocalstack/js-sdk';
80
+ const vs = new VocalStack({ baseUrl: process.env.VOCALSTACK_BASE_URL, apiKey: process.env.VOCALSTACK_API_KEY });
81
+
82
+ const tx = await vs.transcriptions.create({
83
+ url: 'https://raw.githubusercontent.com/openai/whisper/main/tests/jfk.flac',
84
+ name: 'JFK sample',
85
+ });
86
+ const recordingId = tx.job.recordingId;
87
+
88
+ // queued -> fetching -> transcribing -> done | failed
89
+ const job = await vs.jobs.wait(recordingId);
90
+ if (job.state !== 'done') throw new Error(`transcription ${job.state}: ${job.error}`);
91
+
92
+ const { sentences } = await vs.recordings.transcript(recordingId);
93
+ for (const s of sentences) console.log(s.speaker >= 0 ? `[speaker ${s.speaker}] ${s.text}` : s.text);
94
+ console.log('recordingId', recordingId);
95
+ ```
96
+
97
+ ## 2. Transcribe a local file
98
+
99
+ ```js
100
+ import { VocalStack } from '@vocalstack/js-sdk';
101
+ const vs = new VocalStack({ baseUrl: process.env.VOCALSTACK_BASE_URL, apiKey: process.env.VOCALSTACK_API_KEY });
102
+
103
+ const tx = await vs.transcriptions.create({
104
+ file: process.argv[2], // a path (mp3, wav, flac, ogg, m4a, mp4 ...), a Blob or bytes
105
+ settings: { spokenLanguage: 'en', speakerLabels: 'on' }, // both optional; speakerLabels: 'auto' (default) | 'on' | 'off'
106
+ // Off unless asked for: settings.summary, settings.title, settings.summaryTranslations,
107
+ // settings.voice (spoken translations), and shareLink: true (a public share link).
108
+ });
109
+ const job = await vs.jobs.wait(tx.job.recordingId);
110
+ if (job.state !== 'done') throw new Error(`transcription ${job.state}: ${job.error}`);
111
+
112
+ const { sentences } = await vs.recordings.transcript(job.recordingId);
113
+ console.log(sentences.map((s) => s.text).join(' '));
114
+ ```
115
+
116
+ ## 3. Fetch a transcript or an export file
117
+
118
+ `recordings.transcript` returns segments (with word timings), sentences and
119
+ speakers. `recordings.export` returns the file as a `Response`: formats `json`,
120
+ `srt`, `xlsx`, `pdf`; add `{ lang: '<code>' }` for a translated file.
121
+
122
+ ```js
123
+ import { writeFile } from 'node:fs/promises';
124
+ import { VocalStack } from '@vocalstack/js-sdk';
125
+ const vs = new VocalStack({ baseUrl: process.env.VOCALSTACK_BASE_URL, apiKey: process.env.VOCALSTACK_API_KEY });
126
+ const recordingId = process.argv[2];
127
+
128
+ const snap = await vs.recordings.transcript(recordingId);
129
+ console.log(`${snap.segments.length} segments, ${snap.sentences.length} sentences, state ${snap.state}`);
130
+
131
+ const res = await vs.recordings.export(recordingId, 'srt');
132
+ await writeFile('transcript.srt', Buffer.from(await res.arrayBuffer()));
133
+ ```
134
+
135
+ ## 4. Live transcription from a microphone or a file
136
+
137
+ A live transcription takes raw PCM: 32-bit float little-endian, 16 kHz, mono.
138
+ Create it, start a recording, and open the publisher socket the grant names.
139
+ Here ffmpeg decodes a file at realtime speed (`-re`). For a microphone, swap
140
+ the input: `-f avfoundation -i :0` on macOS, `-f pulse -i default` on Linux.
141
+ (`@vocalstack/js-sdk/mic` is for browsers, and browsers must not hold API keys.)
142
+
143
+ ```js
144
+ import { spawn } from 'node:child_process';
145
+ import { VocalStack } from '@vocalstack/js-sdk';
146
+ const vs = new VocalStack({ baseUrl: process.env.VOCALSTACK_BASE_URL, apiKey: process.env.VOCALSTACK_API_KEY });
147
+
148
+ const tx = await vs.transcriptions.createLive({ name: 'Live demo', settings: { spokenLanguage: 'en' } });
149
+ const grant = await vs.transcriptions.startRecording(tx.id);
150
+ const stream = await vs.recordings.publish(grant, { readyDeadlineMs: 180_000 }); // a cold engine can take a while
151
+
152
+ const ff = spawn('ffmpeg', ['-loglevel', 'error', '-re', '-i', process.argv[2],
153
+ '-f', 'f32le', '-ac', '1', '-ar', '16000', '-']);
154
+ ff.stdout.on('data', (chunk) => stream.sendAudio(new Uint8Array(chunk)));
155
+ ff.on('close', () => stream.eof()); // eof() ends the recording
156
+
157
+ for await (const ev of stream) {
158
+ if (ev.type === 'transcript.committed') console.log(`[speaker ${ev.segment.speaker}] ${ev.segment.text}`);
159
+ // also: 'transcript.draft' (replaces the live tail), 'transcript.sentence', 'transcript.translated'
160
+ }
161
+
162
+ // To end a recording without eof(): vs.recordings.pause(id), then vs.recordings.stop(id).
163
+ const { sentences } = await vs.recordings.transcript(grant.recording.id);
164
+ console.log('final:', sentences.map((s) => s.text).join(' '));
165
+ ```
166
+
167
+ To transcribe without storing anything, use a realtime session instead:
168
+ `await vs.realtime.transcribeFile('talk.mp3')` returns `{ text, segments }`.
169
+
170
+ ## 5. Search transcripts
171
+
172
+ ```js
173
+ import { VocalStack } from '@vocalstack/js-sdk';
174
+ const vs = new VocalStack({ baseUrl: process.env.VOCALSTACK_BASE_URL, apiKey: process.env.VOCALSTACK_API_KEY });
175
+
176
+ const { hits } = await vs.search.query(process.argv[2] ?? 'country', { limit: 5 }); // q: 2+ characters
177
+ for (const h of hits) {
178
+ console.log(`${h.linkName} @ ${h.mediaStart.toFixed(1)}s [speaker ${h.speaker}]: ${h.text} (recording ${h.recordingId})`);
179
+ }
180
+ ```
181
+
182
+ ## 6. A batch of files
183
+
184
+ Many files at once cost less as a batch: one create, one minimum charge for
185
+ the whole batch. Its files get transcripts, exports and speaker labels (when
186
+ `settings.speakerLabels` asks), never a summary, title, share link or voices.
187
+
188
+ ```js
189
+ import { VocalStack } from '@vocalstack/js-sdk';
190
+ const vs = new VocalStack({ baseUrl: process.env.VOCALSTACK_BASE_URL, apiKey: process.env.VOCALSTACK_API_KEY });
191
+
192
+ const batch = await vs.batches.create({ name: 'Interviews', settings: { spokenLanguage: 'en' } });
193
+ for (const path of process.argv.slice(2)) await vs.batches.addFile(batch.id, { file: path }); // or addUrl(batch.id, url)
194
+ await vs.batches.start(batch.id);
195
+ const done = await vs.batches.wait(batch.id); // done (or stopped); check done.counts.failed
196
+ for (const tx of done.files ?? []) {
197
+ if (tx.job?.state !== 'done') continue;
198
+ const { sentences } = await vs.recordings.transcript(tx.job.recordingId);
199
+ console.log(tx.name, sentences.map((s) => s.text).join(' '));
200
+ }
201
+ // vs.batches.export(batch.id, 'srt') is every finished transcript in one zip.
202
+ ```
203
+
204
+ ## Errors
205
+
206
+ A failed call throws `VocalStackError` with `code`, `message`, `status`,
207
+ `retryable`, `details` (the body's `data.details`), `headers` and
208
+ `retryAfter` (seconds, from `Retry-After`). A `429`, and a `503 unavailable`
209
+ on a call that is safe to repeat, is retried for you after its `Retry-After`,
210
+ twice by default (`maxRetries` on the client).
211
+
212
+ | Status | `code` | What to do |
213
+ |---|---|---|
214
+ | 401 | `invalid_token`, `key_expired`, `key_revoked` | Check `VOCALSTACK_API_KEY`. Use the replacement key, or create a new one. |
215
+ | 403 | `forbidden` | Usually a missing scope: the message names it, and `err.headers.get('www-authenticate')` lists the scopes the route needs. A suspended key or account gets `suspended`. |
216
+ | 403 | `terms_acceptance_required` | The account has not accepted the current Terms of Service and Privacy Policy: someone signs in to the Dashboard and accepts them (an organisation's keys work once an owner or admin has). Not retryable until then. |
217
+ | 402 | `insufficient_credit` | The account's credit is out, or does not cover the transcription: every transcription is billed at least the plan's minimum (`rates.minimumSeconds`, 5 minutes). `details`: `balance`, `plan`, `topUpUrl`, and `required` when the cost was known up front. Add credit; `vs.billing.get()` shows the balance. |
218
+ | 409 | `payment_method_required` | Auto top-up (`vs.billing.setAutoTopUp`) and the portal (`vs.billing.portal()`) need a payment first (`vs.billing.createCheckout({ kind: 'topup', amount, acceptTerms: true })` opens one; a signed-in person's token, never an API key). |
219
+ | 402 | `quota_exceeded` | The plan's transcriptions-at-once limit is reached. `details`: `metric` (`concurrent_transcriptions`), `limit`, `used`, `plan`, `topUpUrl`. |
220
+ | 429 | `rate_limited` | Retried automatically; if it still fails, wait `err.retryAfter` seconds. Every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`. |
221
+ | 503 | `unavailable` | The service is waking (its database was asleep). Retried automatically after `Retry-After` (2 s) on calls that are safe to repeat: GET, HEAD, DELETE, or any call with `{ idempotencyKey }`. A create without a key is not retried: retry it yourself, with an `idempotencyKey` so it cannot run twice. A realtime socket refused this way throws `unavailable`; a bare 503 there is `capacity_exceeded`. |
222
+
223
+ ```js
224
+ import { VocalStack, VocalStackError } from '@vocalstack/js-sdk';
225
+ const vs = new VocalStack({ baseUrl: process.env.VOCALSTACK_BASE_URL, apiKey: process.env.VOCALSTACK_API_KEY });
226
+
227
+ try {
228
+ await vs.search.query('country');
229
+ } catch (err) {
230
+ if (!(err instanceof VocalStackError)) throw err;
231
+ if (err.code === 'insufficient_credit' || err.code === 'quota_exceeded') console.error(err.message, err.details);
232
+ else console.error(err.status, err.code, err.message);
233
+ }
234
+ ```
235
+
236
+ ## More
237
+
238
+ - The other groups: `vs.transcriptions` (`list`, `get`, `update`, `archive`,
239
+ `restore`, `purge`, `listShareLinks`, `createShareLink`), `vs.recordings`
240
+ (`get`, `summarize`, `summary`, `translation`, `audioUrl`, `subscribe` for a
241
+ live transcript feed), `vs.batches` (`create`, `addFile`, `addUrl`, `start`,
242
+ `wait`, `get`, `list`, `export`, `stop`, `delete`), `vs.corrections`, `vs.shareLinks`, `vs.voices`,
243
+ `vs.broadcasts`, `vs.me` (`get`, `usage`), `vs.languages`,
244
+ `vs.rtmp`, `vs.events`, `vs.meta`. Each method is named after its operation
245
+ in the OpenAPI document (`transcriptions.create` is `POST /v1/transcriptions`).
246
+ - `<base>/llms.txt` lists every operation in a form written for agents.
247
+ `<base>/llms-full.txt` adds parameters, auth mode, tier and scopes.
248
+ - `<base>/docs` is the interactive reference. `<base>/v1/openapi.json` is
249
+ the OpenAPI document; the SDK's types are generated from it.
@@ -0,0 +1,22 @@
1
+ import type { ErrorCode } from '@vocalstack/contract';
2
+ export type ConnectTokenErrorCode = 'invalid_request' | 'invalid_grant' | 'unsupported_grant_type';
3
+ export interface VocalStackErrorOptions {
4
+ retryable?: boolean;
5
+ status?: number;
6
+ details?: unknown;
7
+ headers?: Headers;
8
+ requestId?: string;
9
+ }
10
+ export declare class VocalStackError extends Error {
11
+ readonly code: ErrorCode | ConnectTokenErrorCode;
12
+ readonly retryable: boolean;
13
+ readonly status?: number;
14
+ readonly details?: unknown;
15
+ readonly headers?: Headers;
16
+ readonly requestId?: string;
17
+ constructor(code: ErrorCode | ConnectTokenErrorCode, message: string, opts?: VocalStackErrorOptions);
18
+ get retryAfter(): number | undefined;
19
+ }
20
+ export declare function capacityExceeded(message: string): VocalStackError;
21
+ export declare function fromEnvelope(body: unknown, status?: number, headers?: Headers): VocalStackError;
22
+ export declare function fromOAuth(body: unknown, status?: number, headers?: Headers): VocalStackError;
package/dist/errors.js ADDED
@@ -0,0 +1,56 @@
1
+ export class VocalStackError extends Error {
2
+ code;
3
+ retryable;
4
+ status;
5
+ details;
6
+ headers;
7
+ requestId;
8
+ constructor(code, message, opts = {}) {
9
+ super(message);
10
+ this.name = 'VocalStackError';
11
+ this.code = code;
12
+ this.retryable = opts.retryable ?? false;
13
+ this.status = opts.status;
14
+ this.details = opts.details;
15
+ this.headers = opts.headers;
16
+ this.requestId = opts.requestId;
17
+ }
18
+ get retryAfter() {
19
+ const v = this.headers?.get('retry-after');
20
+ if (v == null || v === '')
21
+ return undefined;
22
+ const n = Number(v);
23
+ if (Number.isFinite(n))
24
+ return n;
25
+ const at = Date.parse(v);
26
+ return Number.isNaN(at) ? undefined : Math.max(0, (at - Date.now()) / 1000);
27
+ }
28
+ }
29
+ export function capacityExceeded(message) {
30
+ return new VocalStackError('capacity_exceeded', message, { retryable: true });
31
+ }
32
+ export function fromEnvelope(body, status, headers) {
33
+ const b = body;
34
+ const requestId = (b && typeof b.requestId === 'string' ? b.requestId : undefined) ?? headers?.get('x-request-id') ?? undefined;
35
+ if (b && typeof b.code === 'string') {
36
+ return new VocalStackError(b.code, b.message ?? b.code, {
37
+ retryable: b.data?.retryable ?? false,
38
+ status,
39
+ details: b.data?.details,
40
+ headers,
41
+ requestId,
42
+ });
43
+ }
44
+ return new VocalStackError('internal', `unexpected error (status ${status})`, { status, headers, requestId });
45
+ }
46
+ export function fromOAuth(body, status, headers) {
47
+ const b = body;
48
+ if (status === 400 && b && typeof b.error === 'string') {
49
+ return new VocalStackError(b.error, b.error_description ?? b.error, {
50
+ status,
51
+ headers,
52
+ requestId: headers?.get('x-request-id') ?? undefined,
53
+ });
54
+ }
55
+ return fromEnvelope(body, status, headers);
56
+ }
package/dist/file.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export type FileInput = string | Uint8Array | ArrayBuffer | Blob | AsyncIterable<Uint8Array>;
2
+ export declare function fileChunks(input: FileInput): AsyncIterable<Uint8Array>;
package/dist/file.js ADDED
@@ -0,0 +1,38 @@
1
+ export async function* fileChunks(input) {
2
+ if (typeof input === 'string') {
3
+ const fsSpecifier = 'node:' + 'fs';
4
+ const { createReadStream } = (await import(/* @vite-ignore */ fsSpecifier));
5
+ for await (const chunk of createReadStream(input)) {
6
+ yield chunk;
7
+ }
8
+ return;
9
+ }
10
+ if (input instanceof Uint8Array) {
11
+ yield input;
12
+ return;
13
+ }
14
+ if (input instanceof ArrayBuffer) {
15
+ yield new Uint8Array(input);
16
+ return;
17
+ }
18
+ if (typeof Blob !== 'undefined' && input instanceof Blob) {
19
+ const reader = input.stream().getReader();
20
+ try {
21
+ for (;;) {
22
+ const { done, value } = await reader.read();
23
+ if (done)
24
+ return;
25
+ if (value)
26
+ yield value;
27
+ }
28
+ }
29
+ finally {
30
+ reader.releaseLock();
31
+ }
32
+ }
33
+ if (input && typeof input === 'object' && Symbol.asyncIterator in input) {
34
+ yield* input;
35
+ return;
36
+ }
37
+ throw new TypeError('transcribeFile: expected a path, Blob, byte array, or async iterable');
38
+ }