n8n-nodes-soniox-api 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Konstantin
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 ADDED
@@ -0,0 +1,196 @@
1
+ # n8n-nodes-soniox-api
2
+
3
+ [![npm version](https://img.shields.io/npm/v/n8n-nodes-soniox-api.svg)](https://www.npmjs.com/package/n8n-nodes-soniox-api)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![n8n Community Node](https://img.shields.io/badge/n8n-Community%20Node-blue)](https://docs.n8n.io/integrations/community-nodes/)
6
+
7
+ This is an n8n community node that integrates [Soniox Speech-to-Text API](https://soniox.com/) — a high-accuracy speech recognition system.
8
+
9
+ [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
10
+
11
+ [Installation](#installation) ·
12
+ [Operations](#operations) ·
13
+ [Credentials](#credentials) ·
14
+ [Usage](#usage) ·
15
+ [Resources](#resources)
16
+
17
+ ## Installation
18
+
19
+ ### n8n Community Nodes
20
+
21
+ 1. Go to **Settings → Community Nodes** in your n8n instance
22
+ 2. Click **Install** and enter: `n8n-nodes-soniox-api`
23
+ 3. Click **Install**
24
+ 4. Restart n8n to load the node
25
+
26
+ ### Manual Installation
27
+
28
+ To get started locally, install the node in your n8n root directory:
29
+
30
+ ```bash
31
+ cd ~/.n8n
32
+ npm install n8n-nodes-soniox-api
33
+ ```
34
+
35
+ For Docker-based n8n installations, add the package to your n8n installation:
36
+
37
+ ```bash
38
+ docker exec -it n8n npm install n8n-nodes-soniox-api
39
+ ```
40
+
41
+ ### Development
42
+
43
+ For local development and testing:
44
+
45
+ ```bash
46
+ git clone https://github.com/mazixs/n8n-nodes-soniox-api.git
47
+ cd n8n-nodes-soniox-api
48
+ npm install
49
+ npm run build
50
+ npm link
51
+
52
+ # Link to your n8n installation
53
+ cd ~/.n8n
54
+ npm link n8n-nodes-soniox-api
55
+ ```
56
+
57
+ ## Operations
58
+
59
+ This node supports the following operations:
60
+
61
+ ### File Operations
62
+ - **Upload** — Upload audio files (multipart/form-data support)
63
+ - **Get** — Retrieve file by ID
64
+ - **Get All** — List all files (with pagination)
65
+ - **Delete** — Delete a file
66
+
67
+ ### Transcription Operations
68
+ - **Create** — Create transcription with configurable parameters:
69
+ - Model selection
70
+ - Language hints
71
+ - Speaker diarization
72
+ - Non-final results
73
+ - **Get** — Retrieve transcription result by ID
74
+ - **Get All** — List all transcriptions (with pagination)
75
+
76
+ ### Model Operations
77
+ - **Get All** — List available speech recognition models
78
+
79
+ ## Credentials
80
+
81
+ ### Setting up Credentials
82
+
83
+ 1. In n8n, navigate to **Credentials → Add Credential → Soniox API**
84
+ 2. Enter your credentials:
85
+ - **API Key**: Get your API key from [console.soniox.com](https://console.soniox.com/)
86
+ - **API URL**: `https://api.soniox.com/v1` (default)
87
+ 3. Click **Save**
88
+
89
+ ## Usage
90
+
91
+ ### Basic Workflow Example
92
+
93
+ Here's a simple workflow to upload an audio file and transcribe it:
94
+
95
+ ```
96
+ [Read Binary File]
97
+
98
+ [Soniox: File Upload]
99
+
100
+ [Soniox: Transcription Create]
101
+
102
+ [Soniox: Transcription Get]
103
+ ```
104
+
105
+ ### Node Configuration
106
+
107
+ **1. Read Binary File**
108
+ - Property Name: `data`
109
+ - File Path: `/path/to/audio.mp3`
110
+
111
+ **2. Soniox: File Upload**
112
+ - Resource: `File`
113
+ - Operation: `Upload`
114
+ - Binary Property: `data`
115
+
116
+ **3. Soniox: Transcription Create**
117
+ - Resource: `Transcription`
118
+ - Operation: `Create`
119
+ - File ID: `{{ $json.fileId }}`
120
+ - Model: `en_v2_lowlatency`
121
+
122
+ **4. Soniox: Transcription Get**
123
+ - Resource: `Transcription`
124
+ - Operation: `Get`
125
+ - Transcription ID: `{{ $json.transcriptionId }}`
126
+
127
+ ## Features
128
+
129
+ - ✅ **Retry Logic** — Automatic retry with exponential backoff for failed requests
130
+ - ✅ **Rate Limiting** — Smart handling of 429 responses with Retry-After headers
131
+ - ✅ **Timeout Control** — Configurable timeouts for API and file upload operations
132
+ - ✅ **Type Safety** — Full TypeScript implementation with n8n-workflow types
133
+ - ✅ **Error Handling** — Comprehensive error messages for debugging
134
+
135
+ ## Resources
136
+
137
+ - [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
138
+ - [Soniox API Documentation](https://soniox.com/docs/stt/api-reference)
139
+ - [Soniox Console](https://console.soniox.com/)
140
+
141
+ ## Development
142
+
143
+ ### Build
144
+
145
+ ```bash
146
+ npm install
147
+ npm run build
148
+ ```
149
+
150
+ ### Lint
151
+
152
+ ```bash
153
+ npm run lint
154
+ npm run lintfix # Auto-fix issues
155
+ ```
156
+
157
+ ### Testing
158
+
159
+ Link the node to your n8n installation:
160
+
161
+ ```bash
162
+ npm run build && npm link
163
+ cd ~/.n8n && npm link n8n-nodes-soniox-api
164
+ n8n start
165
+ ```
166
+
167
+ Then test the node in your n8n workflows.
168
+
169
+ ## Version History
170
+
171
+ See [CHANGELOG.md](./CHANGELOG.md) for detailed release notes.
172
+
173
+ ## License
174
+
175
+ [MIT](LICENSE.md)
176
+
177
+ ## Contributing
178
+
179
+ Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
180
+
181
+ ## Author
182
+
183
+ **mazix**
184
+ - GitHub: [@mazixs](https://github.com/mazixs)
185
+ - npm: [n8n-nodes-soniox-api](https://www.npmjs.com/package/n8n-nodes-soniox-api)
186
+
187
+ ## Support
188
+
189
+ If you encounter issues or have questions:
190
+ 1. Check the [documentation](./docs)
191
+ 2. Search [existing issues](https://github.com/mazixs/n8n-nodes-soniox-api/issues)
192
+ 3. Create a [new issue](https://github.com/mazixs/n8n-nodes-soniox-api/issues/new) if needed
193
+
194
+ ---
195
+
196
+ **Made with ❤️ for the n8n community**
@@ -0,0 +1,10 @@
1
+ import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class SonioxApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ icon: any;
7
+ properties: INodeProperties[];
8
+ authenticate: IAuthenticateGeneric;
9
+ test: ICredentialTestRequest;
10
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SonioxApi = void 0;
4
+ class SonioxApi {
5
+ constructor() {
6
+ this.name = 'sonioxApi';
7
+ this.displayName = 'Soniox API';
8
+ this.documentationUrl = 'https://soniox.com/docs/stt/get-started';
9
+ this.icon = 'file:soniox.svg';
10
+ this.properties = [
11
+ {
12
+ displayName: 'API Key',
13
+ name: 'apiKey',
14
+ type: 'string',
15
+ typeOptions: { password: true },
16
+ default: '',
17
+ required: true,
18
+ description: 'The Soniox API key for authentication',
19
+ },
20
+ {
21
+ displayName: 'API URL',
22
+ name: 'apiUrl',
23
+ type: 'string',
24
+ default: 'https://api.soniox.com/v1',
25
+ description: 'The base URL for Soniox API',
26
+ },
27
+ ];
28
+ this.authenticate = {
29
+ type: 'generic',
30
+ properties: {
31
+ headers: {
32
+ 'Authorization': '={{`Bearer ${$credentials.apiKey}`}}',
33
+ },
34
+ },
35
+ };
36
+ this.test = {
37
+ request: {
38
+ baseURL: '={{$credentials.apiUrl}}',
39
+ url: '/models',
40
+ method: 'GET',
41
+ },
42
+ };
43
+ }
44
+ }
45
+ exports.SonioxApi = SonioxApi;
@@ -0,0 +1,11 @@
1
+ <svg viewBox="-5 -15 110 50" xmlns="http://www.w3.org/2000/svg">
2
+ <title>Soniox</title>
3
+ <g fill="#FFFFFF">
4
+ <path d="m0 14.866 2.1606-3.5214c1.8927 1.2576 3.9669 1.8995 5.6694 1.8995 1.0025 0 1.4606-0.3036 1.4606-0.8847v-0.0607c0-0.6419-0.9161-0.9194-2.6532-1.4138-3.2582-0.8587-5.8509-1.9602-5.8509-5.2995v-0.06938c0-3.5214 2.8088-5.4903 6.6114-5.4903 2.4112 0 4.9089 0.70255 6.8016 1.9342l-1.9791 3.6775c-1.7112-0.95408-3.5693-1.5352-4.8744-1.5352-0.88152 0-1.3396 0.33827-1.3396 0.79796v0.06071c0 0.64184 0.94202 0.95409 2.6792 1.4745 3.2582 0.91939 5.8509 2.0556 5.8509 5.2735v0.0607c0 3.6515-2.7137 5.551-6.741 5.551-2.7656-0.0087-5.5052-0.798-7.7955-2.4546z"/>
5
+ <path d="m16.135 8.7342v-0.06071c0-4.7184 3.8372-8.6735 9.1436-8.6735 5.2719 0 9.0832 3.8944 9.0832 8.6127v0.06072c0 4.7184-3.8372 8.6735-9.1437 8.6735-5.2718 0-9.0831-3.8944-9.0831-8.6128zm12.583 0v-0.06071c0-2.0209-1.4606-3.7383-3.5088-3.7383-2.1001 0-3.4483 1.6826-3.4483 3.6775v0.06072c0 2.0209 1.4605 3.7383 3.5088 3.7383 2.1087 0 3.4483-1.6827 3.4483-3.6776z"/>
6
+ <path d="m36.877 0.36428h5.7904v2.3332c1.063-1.3791 2.5927-2.6974 4.9348-2.6974 3.5089 0 5.609 2.3332 5.609 6.0974v10.85h-5.7905v-8.977c0-1.8041-0.942-2.7929-2.3161-2.7929-1.4001 0-2.4372 0.9801-2.4372 2.7929v8.977h-5.7904z"/>
7
+ <path d="m55.951 0.36426h5.7904v16.584h-5.7904z"/>
8
+ <path d="m64.29 8.7342v-0.06071c0-4.7184 3.8373-8.6735 9.1437-8.6735 5.2719 0 9.0832 3.8944 9.0832 8.6127v0.06072c0 4.7184-3.8372 8.6735-9.1437 8.6735-5.2719 0-9.0832-3.8944-9.0832-8.6128zm12.592 0v-0.06071c0-2.0209-1.4605-3.7383-3.5088-3.7383-2.1001 0-3.4483 1.6826-3.4483 3.6775v0.06072c0 2.0209 1.4606 3.7383 3.5088 3.7383 2.1088 0 3.4483-1.6827 3.4483-3.6776z"/>
9
+ <path d="m88.082 8.578-5.4533-8.2138h6.2484l2.4372 4.0765 2.4371-4.0765h6.1275l-5.4274 8.1791 5.5484 8.3959h-6.2225l-2.5582-4.2587-2.5927 4.2587h-6.0929z"/>
10
+ </g>
11
+ </svg>
@@ -0,0 +1,3 @@
1
+ import { IExecuteFunctions, IHookFunctions, ILoadOptionsFunctions, IDataObject, IHttpRequestMethods } from 'n8n-workflow';
2
+ export declare function sonioxApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject, uri?: string, option?: IDataObject): Promise<any>;
3
+ export declare function sonioxApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: IDataObject): Promise<any>;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sonioxApiRequest = sonioxApiRequest;
4
+ exports.sonioxApiRequestAllItems = sonioxApiRequestAllItems;
5
+ const n8n_workflow_1 = require("n8n-workflow");
6
+ const constants_1 = require("./constants");
7
+ /**
8
+ * Задержка с exponential backoff
9
+ */
10
+ async function delay(attempt) {
11
+ const delayMs = Math.min(constants_1.RETRY_CONFIG.BASE_DELAY * Math.pow(constants_1.RETRY_CONFIG.BACKOFF_MULTIPLIER, attempt), constants_1.RETRY_CONFIG.MAX_DELAY);
12
+ return new Promise((resolve) => setTimeout(resolve, delayMs));
13
+ }
14
+ async function sonioxApiRequest(method, endpoint, body = {}, qs = {}, uri, option = {}) {
15
+ var _a, _b;
16
+ const credentials = await this.getCredentials('sonioxApi');
17
+ const options = {
18
+ method,
19
+ qs,
20
+ uri: uri || `${credentials.apiUrl}${endpoint}`,
21
+ json: true,
22
+ timeout: option.formData ? constants_1.TIMEOUTS.FILE_UPLOAD : constants_1.TIMEOUTS.API_REQUEST,
23
+ };
24
+ // Headers - Authorization будет добавлен через credentials.authenticate
25
+ options.headers = {};
26
+ // Handle multipart/form-data (для file upload)
27
+ if (option.formData) {
28
+ options.formData = option.formData;
29
+ // Content-Type устанавливается автоматически для multipart
30
+ }
31
+ else {
32
+ // Обычный JSON request
33
+ options.body = body;
34
+ options.headers['Content-Type'] = constants_1.CONTENT_TYPES.JSON;
35
+ }
36
+ // Retry логика с exponential backoff
37
+ let lastError;
38
+ for (let attempt = 0; attempt <= constants_1.RETRY_CONFIG.MAX_RETRIES; attempt++) {
39
+ try {
40
+ return await this.helpers.requestWithAuthentication.call(this, 'sonioxApi', options);
41
+ }
42
+ catch (error) {
43
+ lastError = error;
44
+ const statusCode = error.statusCode || ((_a = error.response) === null || _a === void 0 ? void 0 : _a.statusCode);
45
+ // Проверяем, нужен ли retry
46
+ const shouldRetry = attempt < constants_1.RETRY_CONFIG.MAX_RETRIES &&
47
+ (constants_1.RETRYABLE_STATUS_CODES.includes(statusCode) || error.code === 'ETIMEDOUT' || error.code === 'ECONNRESET');
48
+ if (!shouldRetry) {
49
+ break;
50
+ }
51
+ // Обработка rate limiting (429)
52
+ if (statusCode === 429) {
53
+ const retryAfter = (_b = error.response) === null || _b === void 0 ? void 0 : _b.headers['retry-after'];
54
+ if (retryAfter) {
55
+ const waitMs = parseInt(retryAfter, 10) * 1000;
56
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
57
+ continue;
58
+ }
59
+ }
60
+ // Exponential backoff для остальных ошибок
61
+ await delay(attempt);
62
+ }
63
+ }
64
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), lastError);
65
+ }
66
+ async function sonioxApiRequestAllItems(method, endpoint, body = {}, qs = {}) {
67
+ const returnData = [];
68
+ let responseData;
69
+ qs.limit = constants_1.API_LIMITS.PAGINATION_LIMIT;
70
+ qs.offset = 0;
71
+ do {
72
+ responseData = await sonioxApiRequest.call(this, method, endpoint, body, qs);
73
+ returnData.push(...responseData.items);
74
+ qs.offset = qs.offset + qs.limit;
75
+ } while (responseData.items.length !== 0);
76
+ return returnData;
77
+ }
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeType, INodeTypeDescription, INodeExecutionData } from 'n8n-workflow';
2
+ export declare class Soniox implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Soniox = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const FileDescription_1 = require("./descriptions/FileDescription");
6
+ const TranscriptionDescription_1 = require("./descriptions/TranscriptionDescription");
7
+ const ModelDescription_1 = require("./descriptions/ModelDescription");
8
+ const GenericFunctions_1 = require("./GenericFunctions");
9
+ const constants_1 = require("./constants");
10
+ class Soniox {
11
+ constructor() {
12
+ this.description = {
13
+ displayName: 'Soniox',
14
+ name: 'soniox',
15
+ icon: 'file:soniox.svg',
16
+ group: ['transform'],
17
+ version: 1,
18
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
19
+ description: 'Interact with Soniox Speech-to-Text API',
20
+ defaults: {
21
+ name: 'Soniox',
22
+ },
23
+ inputs: ['main'],
24
+ outputs: ['main'],
25
+ credentials: [
26
+ {
27
+ name: 'sonioxApi',
28
+ required: true,
29
+ },
30
+ ],
31
+ properties: [
32
+ {
33
+ displayName: 'Resource',
34
+ name: 'resource',
35
+ type: 'options',
36
+ noDataExpression: true,
37
+ options: [
38
+ {
39
+ name: 'File',
40
+ value: 'file',
41
+ },
42
+ {
43
+ name: 'Model',
44
+ value: 'model',
45
+ },
46
+ {
47
+ name: 'Transcription',
48
+ value: 'transcription',
49
+ },
50
+ ],
51
+ default: 'transcription',
52
+ },
53
+ ...FileDescription_1.fileOperations,
54
+ ...FileDescription_1.fileFields,
55
+ ...TranscriptionDescription_1.transcriptionOperations,
56
+ ...TranscriptionDescription_1.transcriptionFields,
57
+ ...ModelDescription_1.modelOperations,
58
+ ...ModelDescription_1.modelFields,
59
+ ],
60
+ };
61
+ }
62
+ async execute() {
63
+ const items = this.getInputData();
64
+ const returnData = [];
65
+ const resource = this.getNodeParameter('resource', 0);
66
+ const operation = this.getNodeParameter('operation', 0);
67
+ for (let i = 0; i < items.length; i++) {
68
+ try {
69
+ if (resource === 'file') {
70
+ if (operation === 'upload') {
71
+ // 1. Получить параметры
72
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
73
+ const fileName = this.getNodeParameter('fileName', i, '');
74
+ // 2. Валидация binary data
75
+ const itemBinary = items[i].binary;
76
+ if (!itemBinary) {
77
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No binary data exists on input item. Please connect a node that provides binary data.', { itemIndex: i });
78
+ }
79
+ const binaryData = itemBinary[binaryPropertyName];
80
+ if (!binaryData) {
81
+ const availableProperties = Object.keys(itemBinary);
82
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Binary property "${binaryPropertyName}" not found. Available: ${availableProperties.join(', ')}`, { itemIndex: i });
83
+ }
84
+ // 3. Получить Buffer из binary data
85
+ const buffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
86
+ // 4. Определить имя файла
87
+ const uploadFileName = fileName || binaryData.fileName || 'file';
88
+ // 5. Подготовить formData для multipart/form-data
89
+ const formData = {
90
+ file: {
91
+ value: buffer,
92
+ options: {
93
+ filename: uploadFileName,
94
+ contentType: binaryData.mimeType || constants_1.CONTENT_TYPES.BINARY,
95
+ },
96
+ },
97
+ };
98
+ // 6. Upload через API
99
+ const response = await GenericFunctions_1.sonioxApiRequest.call(this, 'POST', '/files', {}, {}, undefined, { formData });
100
+ // 7. Вернуть результат
101
+ returnData.push({
102
+ json: {
103
+ fileId: response.file_id,
104
+ fileName: uploadFileName,
105
+ mimeType: binaryData.mimeType,
106
+ fileSize: binaryData.fileSize,
107
+ uploadedAt: new Date().toISOString(),
108
+ },
109
+ });
110
+ }
111
+ else if (operation === 'get') {
112
+ const fileId = this.getNodeParameter('fileId', i);
113
+ const response = await GenericFunctions_1.sonioxApiRequest.call(this, 'GET', `/files/${fileId}`);
114
+ returnData.push({ json: response });
115
+ }
116
+ else if (operation === 'getAll') {
117
+ const returnAll = this.getNodeParameter('returnAll', i);
118
+ let responseData;
119
+ if (returnAll) {
120
+ responseData = await GenericFunctions_1.sonioxApiRequestAllItems.call(this, 'GET', '/files');
121
+ }
122
+ else {
123
+ const limit = this.getNodeParameter('limit', i);
124
+ responseData = await GenericFunctions_1.sonioxApiRequest.call(this, 'GET', '/files', {}, { limit });
125
+ }
126
+ const fileItems = Array.isArray(responseData) ? responseData : responseData.items || [];
127
+ fileItems.forEach((item) => {
128
+ returnData.push({ json: item });
129
+ });
130
+ }
131
+ else if (operation === 'delete') {
132
+ const fileId = this.getNodeParameter('fileId', i);
133
+ await GenericFunctions_1.sonioxApiRequest.call(this, 'DELETE', `/files/${fileId}`);
134
+ returnData.push({
135
+ json: {
136
+ success: true,
137
+ fileId,
138
+ },
139
+ });
140
+ }
141
+ }
142
+ else if (resource === 'transcription') {
143
+ if (operation === 'create') {
144
+ const fileId = this.getNodeParameter('fileId', i);
145
+ const model = this.getNodeParameter('model', i, '');
146
+ const additionalFields = this.getNodeParameter('additionalFields', i, {});
147
+ const body = {
148
+ file_id: fileId,
149
+ };
150
+ if (model) {
151
+ body.model = model;
152
+ }
153
+ if (additionalFields.language) {
154
+ body.language = additionalFields.language;
155
+ }
156
+ if (additionalFields.enableSpeakerDiarization) {
157
+ body.enable_speaker_diarization = additionalFields.enableSpeakerDiarization;
158
+ }
159
+ if (additionalFields.includeNonFinal) {
160
+ body.include_nonfinal = additionalFields.includeNonFinal;
161
+ }
162
+ const response = await GenericFunctions_1.sonioxApiRequest.call(this, 'POST', '/transcriptions', body);
163
+ returnData.push({ json: response });
164
+ }
165
+ else if (operation === 'get') {
166
+ const transcriptionId = this.getNodeParameter('transcriptionId', i);
167
+ const response = await GenericFunctions_1.sonioxApiRequest.call(this, 'GET', `/transcriptions/${transcriptionId}`);
168
+ returnData.push({ json: response });
169
+ }
170
+ else if (operation === 'getAll') {
171
+ const returnAll = this.getNodeParameter('returnAll', i);
172
+ let responseData;
173
+ if (returnAll) {
174
+ responseData = await GenericFunctions_1.sonioxApiRequestAllItems.call(this, 'GET', '/transcriptions');
175
+ }
176
+ else {
177
+ const limit = this.getNodeParameter('limit', i);
178
+ responseData = await GenericFunctions_1.sonioxApiRequest.call(this, 'GET', '/transcriptions', {}, { limit });
179
+ }
180
+ const transcriptionItems = Array.isArray(responseData) ? responseData : responseData.items || [];
181
+ transcriptionItems.forEach((item) => {
182
+ returnData.push({ json: item });
183
+ });
184
+ }
185
+ }
186
+ else if (resource === 'model') {
187
+ if (operation === 'getAll') {
188
+ const response = await GenericFunctions_1.sonioxApiRequest.call(this, 'GET', '/models');
189
+ const models = Array.isArray(response) ? response : response.models || [];
190
+ models.forEach((model) => {
191
+ returnData.push({ json: model });
192
+ });
193
+ }
194
+ }
195
+ }
196
+ catch (error) {
197
+ if (this.continueOnFail()) {
198
+ returnData.push({ json: { error: error.message } });
199
+ continue;
200
+ }
201
+ throw error;
202
+ }
203
+ }
204
+ return [returnData];
205
+ }
206
+ }
207
+ exports.Soniox = Soniox;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Константы для Soniox API
3
+ */
4
+ /**
5
+ * Лимиты API
6
+ */
7
+ export declare const API_LIMITS: {
8
+ /** Максимальное количество элементов на запрос */
9
+ MAX_ITEMS_PER_REQUEST: number;
10
+ /** Количество элементов по умолчанию */
11
+ DEFAULT_LIMIT: number;
12
+ /** Лимит для пагинации */
13
+ PAGINATION_LIMIT: number;
14
+ };
15
+ /**
16
+ * MIME типы
17
+ */
18
+ export declare const CONTENT_TYPES: {
19
+ JSON: string;
20
+ BINARY: string;
21
+ FORM_DATA: string;
22
+ };
23
+ /**
24
+ * Настройки retry для API запросов
25
+ */
26
+ export declare const RETRY_CONFIG: {
27
+ /** Максимальное количество попыток */
28
+ MAX_RETRIES: number;
29
+ /** Базовая задержка в мс */
30
+ BASE_DELAY: number;
31
+ /** Максимальная задержка в мс */
32
+ MAX_DELAY: number;
33
+ /** Множитель для exponential backoff */
34
+ BACKOFF_MULTIPLIER: number;
35
+ };
36
+ /**
37
+ * Таймауты
38
+ */
39
+ export declare const TIMEOUTS: {
40
+ /** Таймаут для API запросов в мс */
41
+ API_REQUEST: number;
42
+ /** Таймаут для загрузки файлов в мс */
43
+ FILE_UPLOAD: number;
44
+ };
45
+ /**
46
+ * HTTP коды статуса, которые требуют retry
47
+ */
48
+ export declare const RETRYABLE_STATUS_CODES: number[];
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ /**
3
+ * Константы для Soniox API
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RETRYABLE_STATUS_CODES = exports.TIMEOUTS = exports.RETRY_CONFIG = exports.CONTENT_TYPES = exports.API_LIMITS = void 0;
7
+ /**
8
+ * Лимиты API
9
+ */
10
+ exports.API_LIMITS = {
11
+ /** Максимальное количество элементов на запрос */
12
+ MAX_ITEMS_PER_REQUEST: 100,
13
+ /** Количество элементов по умолчанию */
14
+ DEFAULT_LIMIT: 50,
15
+ /** Лимит для пагинации */
16
+ PAGINATION_LIMIT: 100,
17
+ };
18
+ /**
19
+ * MIME типы
20
+ */
21
+ exports.CONTENT_TYPES = {
22
+ JSON: 'application/json',
23
+ BINARY: 'application/octet-stream',
24
+ FORM_DATA: 'multipart/form-data',
25
+ };
26
+ /**
27
+ * Настройки retry для API запросов
28
+ */
29
+ exports.RETRY_CONFIG = {
30
+ /** Максимальное количество попыток */
31
+ MAX_RETRIES: 3,
32
+ /** Базовая задержка в мс */
33
+ BASE_DELAY: 1000,
34
+ /** Максимальная задержка в мс */
35
+ MAX_DELAY: 10000,
36
+ /** Множитель для exponential backoff */
37
+ BACKOFF_MULTIPLIER: 2,
38
+ };
39
+ /**
40
+ * Таймауты
41
+ */
42
+ exports.TIMEOUTS = {
43
+ /** Таймаут для API запросов в мс */
44
+ API_REQUEST: 30000,
45
+ /** Таймаут для загрузки файлов в мс */
46
+ FILE_UPLOAD: 60000,
47
+ };
48
+ /**
49
+ * HTTP коды статуса, которые требуют retry
50
+ */
51
+ exports.RETRYABLE_STATUS_CODES = [408, 429, 500, 502, 503, 504];
@@ -0,0 +1,3 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const fileOperations: INodeProperties[];
3
+ export declare const fileFields: INodeProperties[];
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fileFields = exports.fileOperations = void 0;
4
+ const constants_1 = require("../constants");
5
+ exports.fileOperations = [
6
+ {
7
+ displayName: 'Operation',
8
+ name: 'operation',
9
+ type: 'options',
10
+ noDataExpression: true,
11
+ displayOptions: {
12
+ show: {
13
+ resource: ['file'],
14
+ },
15
+ },
16
+ options: [
17
+ {
18
+ name: 'Upload',
19
+ value: 'upload',
20
+ description: 'Upload an audio file',
21
+ action: 'Upload a file',
22
+ },
23
+ {
24
+ name: 'Get',
25
+ value: 'get',
26
+ description: 'Get a file by ID',
27
+ action: 'Get a file',
28
+ },
29
+ {
30
+ name: 'Get All',
31
+ value: 'getAll',
32
+ description: 'Get all files',
33
+ action: 'Get all files',
34
+ },
35
+ {
36
+ name: 'Delete',
37
+ value: 'delete',
38
+ description: 'Delete a file',
39
+ action: 'Delete a file',
40
+ },
41
+ ],
42
+ default: 'upload',
43
+ },
44
+ ];
45
+ exports.fileFields = [
46
+ // Upload operation
47
+ {
48
+ displayName: 'Binary Property',
49
+ name: 'binaryPropertyName',
50
+ type: 'string',
51
+ default: 'data',
52
+ required: true,
53
+ displayOptions: {
54
+ show: {
55
+ resource: ['file'],
56
+ operation: ['upload'],
57
+ },
58
+ },
59
+ description: 'Name of the binary property containing the file',
60
+ },
61
+ {
62
+ displayName: 'File Name',
63
+ name: 'fileName',
64
+ type: 'string',
65
+ default: '',
66
+ displayOptions: {
67
+ show: {
68
+ resource: ['file'],
69
+ operation: ['upload'],
70
+ },
71
+ },
72
+ description: 'Name of the file to upload',
73
+ },
74
+ // Get/Delete operations
75
+ {
76
+ displayName: 'File ID',
77
+ name: 'fileId',
78
+ type: 'string',
79
+ default: '',
80
+ required: true,
81
+ displayOptions: {
82
+ show: {
83
+ resource: ['file'],
84
+ operation: ['get', 'delete'],
85
+ },
86
+ },
87
+ description: 'The ID of the file',
88
+ },
89
+ // Get All operation
90
+ {
91
+ displayName: 'Return All',
92
+ name: 'returnAll',
93
+ type: 'boolean',
94
+ displayOptions: {
95
+ show: {
96
+ resource: ['file'],
97
+ operation: ['getAll'],
98
+ },
99
+ },
100
+ default: false,
101
+ description: 'Whether to return all results or only up to a given limit',
102
+ },
103
+ {
104
+ displayName: 'Limit',
105
+ name: 'limit',
106
+ type: 'number',
107
+ displayOptions: {
108
+ show: {
109
+ resource: ['file'],
110
+ operation: ['getAll'],
111
+ returnAll: [false],
112
+ },
113
+ },
114
+ typeOptions: {
115
+ minValue: 1,
116
+ maxValue: constants_1.API_LIMITS.MAX_ITEMS_PER_REQUEST,
117
+ },
118
+ default: constants_1.API_LIMITS.DEFAULT_LIMIT,
119
+ description: 'Max number of results to return',
120
+ },
121
+ ];
@@ -0,0 +1,3 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const modelOperations: INodeProperties[];
3
+ export declare const modelFields: INodeProperties[];
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.modelFields = exports.modelOperations = void 0;
4
+ exports.modelOperations = [
5
+ {
6
+ displayName: 'Operation',
7
+ name: 'operation',
8
+ type: 'options',
9
+ noDataExpression: true,
10
+ displayOptions: {
11
+ show: {
12
+ resource: ['model'],
13
+ },
14
+ },
15
+ options: [
16
+ {
17
+ name: 'Get All',
18
+ value: 'getAll',
19
+ description: 'Get all available models',
20
+ action: 'Get all models',
21
+ },
22
+ ],
23
+ default: 'getAll',
24
+ },
25
+ ];
26
+ exports.modelFields = [];
@@ -0,0 +1,3 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const transcriptionOperations: INodeProperties[];
3
+ export declare const transcriptionFields: INodeProperties[];
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transcriptionFields = exports.transcriptionOperations = void 0;
4
+ const constants_1 = require("../constants");
5
+ exports.transcriptionOperations = [
6
+ {
7
+ displayName: 'Operation',
8
+ name: 'operation',
9
+ type: 'options',
10
+ noDataExpression: true,
11
+ displayOptions: {
12
+ show: {
13
+ resource: ['transcription'],
14
+ },
15
+ },
16
+ options: [
17
+ {
18
+ name: 'Create',
19
+ value: 'create',
20
+ description: 'Create a transcription',
21
+ action: 'Create a transcription',
22
+ },
23
+ {
24
+ name: 'Get',
25
+ value: 'get',
26
+ description: 'Get a transcription',
27
+ action: 'Get a transcription',
28
+ },
29
+ {
30
+ name: 'Get All',
31
+ value: 'getAll',
32
+ description: 'Get all transcriptions',
33
+ action: 'Get all transcriptions',
34
+ },
35
+ ],
36
+ default: 'create',
37
+ },
38
+ ];
39
+ exports.transcriptionFields = [
40
+ // Create operation
41
+ {
42
+ displayName: 'File ID',
43
+ name: 'fileId',
44
+ type: 'string',
45
+ default: '',
46
+ required: true,
47
+ displayOptions: {
48
+ show: {
49
+ resource: ['transcription'],
50
+ operation: ['create'],
51
+ },
52
+ },
53
+ description: 'The ID of the file to transcribe',
54
+ },
55
+ {
56
+ displayName: 'Model',
57
+ name: 'model',
58
+ type: 'string',
59
+ default: '',
60
+ displayOptions: {
61
+ show: {
62
+ resource: ['transcription'],
63
+ operation: ['create'],
64
+ },
65
+ },
66
+ description: 'The model to use for transcription',
67
+ placeholder: 'e.g., en_v2_lowlatency',
68
+ },
69
+ {
70
+ displayName: 'Additional Fields',
71
+ name: 'additionalFields',
72
+ type: 'collection',
73
+ placeholder: 'Add Field',
74
+ default: {},
75
+ displayOptions: {
76
+ show: {
77
+ resource: ['transcription'],
78
+ operation: ['create'],
79
+ },
80
+ },
81
+ options: [
82
+ {
83
+ displayName: 'Language',
84
+ name: 'language',
85
+ type: 'string',
86
+ default: '',
87
+ description: 'Language code (e.g., en, es, fr)',
88
+ },
89
+ {
90
+ displayName: 'Enable Speaker Diarization',
91
+ name: 'enableSpeakerDiarization',
92
+ type: 'boolean',
93
+ default: false,
94
+ description: 'Whether to enable speaker diarization',
95
+ },
96
+ {
97
+ displayName: 'Include Non-Final',
98
+ name: 'includeNonFinal',
99
+ type: 'boolean',
100
+ default: false,
101
+ description: 'Whether to include non-final results',
102
+ },
103
+ ],
104
+ },
105
+ // Get operation
106
+ {
107
+ displayName: 'Transcription ID',
108
+ name: 'transcriptionId',
109
+ type: 'string',
110
+ default: '',
111
+ required: true,
112
+ displayOptions: {
113
+ show: {
114
+ resource: ['transcription'],
115
+ operation: ['get'],
116
+ },
117
+ },
118
+ description: 'The ID of the transcription',
119
+ },
120
+ // Get All operation
121
+ {
122
+ displayName: 'Return All',
123
+ name: 'returnAll',
124
+ type: 'boolean',
125
+ displayOptions: {
126
+ show: {
127
+ resource: ['transcription'],
128
+ operation: ['getAll'],
129
+ },
130
+ },
131
+ default: false,
132
+ description: 'Whether to return all results or only up to a given limit',
133
+ },
134
+ {
135
+ displayName: 'Limit',
136
+ name: 'limit',
137
+ type: 'number',
138
+ displayOptions: {
139
+ show: {
140
+ resource: ['transcription'],
141
+ operation: ['getAll'],
142
+ returnAll: [false],
143
+ },
144
+ },
145
+ typeOptions: {
146
+ minValue: 1,
147
+ maxValue: constants_1.API_LIMITS.MAX_ITEMS_PER_REQUEST,
148
+ },
149
+ default: constants_1.API_LIMITS.DEFAULT_LIMIT,
150
+ description: 'Max number of results to return',
151
+ },
152
+ ];
@@ -0,0 +1,11 @@
1
+ <svg viewBox="-5 -15 110 50" xmlns="http://www.w3.org/2000/svg">
2
+ <title>Soniox</title>
3
+ <g fill="#FFFFFF">
4
+ <path d="m0 14.866 2.1606-3.5214c1.8927 1.2576 3.9669 1.8995 5.6694 1.8995 1.0025 0 1.4606-0.3036 1.4606-0.8847v-0.0607c0-0.6419-0.9161-0.9194-2.6532-1.4138-3.2582-0.8587-5.8509-1.9602-5.8509-5.2995v-0.06938c0-3.5214 2.8088-5.4903 6.6114-5.4903 2.4112 0 4.9089 0.70255 6.8016 1.9342l-1.9791 3.6775c-1.7112-0.95408-3.5693-1.5352-4.8744-1.5352-0.88152 0-1.3396 0.33827-1.3396 0.79796v0.06071c0 0.64184 0.94202 0.95409 2.6792 1.4745 3.2582 0.91939 5.8509 2.0556 5.8509 5.2735v0.0607c0 3.6515-2.7137 5.551-6.741 5.551-2.7656-0.0087-5.5052-0.798-7.7955-2.4546z"/>
5
+ <path d="m16.135 8.7342v-0.06071c0-4.7184 3.8372-8.6735 9.1436-8.6735 5.2719 0 9.0832 3.8944 9.0832 8.6127v0.06072c0 4.7184-3.8372 8.6735-9.1437 8.6735-5.2718 0-9.0831-3.8944-9.0831-8.6128zm12.583 0v-0.06071c0-2.0209-1.4606-3.7383-3.5088-3.7383-2.1001 0-3.4483 1.6826-3.4483 3.6775v0.06072c0 2.0209 1.4605 3.7383 3.5088 3.7383 2.1087 0 3.4483-1.6827 3.4483-3.6776z"/>
6
+ <path d="m36.877 0.36428h5.7904v2.3332c1.063-1.3791 2.5927-2.6974 4.9348-2.6974 3.5089 0 5.609 2.3332 5.609 6.0974v10.85h-5.7905v-8.977c0-1.8041-0.942-2.7929-2.3161-2.7929-1.4001 0-2.4372 0.9801-2.4372 2.7929v8.977h-5.7904z"/>
7
+ <path d="m55.951 0.36426h5.7904v16.584h-5.7904z"/>
8
+ <path d="m64.29 8.7342v-0.06071c0-4.7184 3.8373-8.6735 9.1437-8.6735 5.2719 0 9.0832 3.8944 9.0832 8.6127v0.06072c0 4.7184-3.8372 8.6735-9.1437 8.6735-5.2719 0-9.0832-3.8944-9.0832-8.6128zm12.592 0v-0.06071c0-2.0209-1.4605-3.7383-3.5088-3.7383-2.1001 0-3.4483 1.6826-3.4483 3.6775v0.06072c0 2.0209 1.4606 3.7383 3.5088 3.7383 2.1088 0 3.4483-1.6827 3.4483-3.6776z"/>
9
+ <path d="m88.082 8.578-5.4533-8.2138h6.2484l2.4372 4.0765 2.4371-4.0765h6.1275l-5.4274 8.1791 5.5484 8.3959h-6.2225l-2.5582-4.2587-2.5927 4.2587h-6.0929z"/>
10
+ </g>
11
+ </svg>
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // This file is intentionally empty.
2
+ // n8n will load the nodes from the paths specified in package.json
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "n8n-nodes-soniox-api",
3
+ "version": "0.2.0",
4
+ "description": "n8n node for Soniox Speech-to-Text API",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "build": "tsc && gulp build:icons",
8
+ "dev": "tsc --watch",
9
+ "lint": "eslint nodes credentials --ext .ts",
10
+ "lintfix": "eslint nodes credentials --ext .ts --fix",
11
+ "prepublishOnly": "npm run build && npm run lint"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "n8n": {
17
+ "n8nNodesApiVersion": 1,
18
+ "credentials": [
19
+ "dist/credentials/SonioxApi.credentials.js"
20
+ ],
21
+ "nodes": [
22
+ "dist/nodes/Soniox/Soniox.node.js"
23
+ ]
24
+ },
25
+ "keywords": [
26
+ "n8n-community-node-package",
27
+ "n8n",
28
+ "soniox",
29
+ "speech-to-text",
30
+ "transcription",
31
+ "audio",
32
+ "voice-recognition",
33
+ "stt",
34
+ "asr",
35
+ "workflow-automation"
36
+ ],
37
+ "license": "MIT",
38
+ "homepage": "https://github.com/mazixs/n8n-nodes-soniox-api",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/mazixs/n8n-nodes-soniox-api.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/mazixs/n8n-nodes-soniox-api/issues"
45
+ },
46
+ "author": {
47
+ "name": "mazix",
48
+ "email": "mazix@bk.ru"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^22.0.0",
52
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
53
+ "@typescript-eslint/parser": "^8.0.0",
54
+ "eslint": "^9.0.0",
55
+ "typescript": "^5.9.0",
56
+ "n8n-workflow": "^1.112.0",
57
+ "n8n-core": "^1.113.0",
58
+ "gulp": "^5.0.0"
59
+ },
60
+ "peerDependencies": {
61
+ "n8n-workflow": "*"
62
+ },
63
+ "overrides": {
64
+ "form-data": "^4.0.4"
65
+ }
66
+ }