@reqvet-sdk/sdk 2.2.0 → 2.2.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.2.1
4
+
5
+ - **Feat** : ajout de `getSignedUploadUrl(fileName, contentType)` — obtenir une URL presignée Supabase pour uploader l'audio directement, sans passer par `/api/v1/upload` (Vercel Serverless Function, limite ~4.5 MB). Recommandé pour les proxies serveur (Next.js, Express…) gérant des fichiers > 4 MB.
6
+ - **Types** : ajout de `SignedUploadResult` dans `index.d.ts`.
7
+ - **Docs** : `uploadAudio()` annotée avec l'avertissement Vercel dans `index.js`, `index.d.ts`, `SDK_REFERENCE.md` et `README.md`.
8
+ - **Examples** : `nextjs/route-generate.ts` et `nextjs/route-generate.mjs` mis à jour pour utiliser `getSignedUploadUrl()`.
9
+ - **Security** : exemple proxy dans `SECURITY.md` mis à jour.
10
+
3
11
  ## 2.2.0
4
12
 
5
13
  - **Feat** : ajout de `listJobs(options?)` — liste les jobs avec pagination (`limit`, `offset`) et filtres (`status`, `sort`, `order`). Aligne le SDK sur `GET /api/v1/jobs`.
package/README.md CHANGED
@@ -1,78 +1,96 @@
1
- # @reqvet/sdk
1
+ # @reqvet-sdk/sdk
2
2
 
3
- Official JavaScript/TypeScript SDK for the [ReqVet](https://reqvet.com) API AI-powered veterinary report generation from audio recordings.
3
+ SDK JavaScript/TypeScript officiel pour l'API [ReqVet](https://reqvet.com) — génération de comptes rendus vétérinaires par IA à partir d'enregistrements audio.
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/@reqvet/sdk)](https://www.npmjs.com/package/@reqvet/sdk)
5
+ [![npm version](https://img.shields.io/npm/v/@reqvet-sdk/sdk)](https://www.npmjs.com/package/@reqvet-sdk/sdk)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
7
  [![Node.js ≥ 18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
8
8
 
9
- ## What it does
9
+ ## Fonctionnalités
10
10
 
11
- - **Upload** an audio recording (`uploadAudio`)
12
- - **Generate** a veterinary report (`createJob`, `generateReport`)
13
- - **Track** jobs — webhook-first or polling (`getJob`, `waitForJob`, `listJobs`)
14
- - **Amend** a completed report with additional audio (`amendJob`)
15
- - **Regenerate** with new instructions (`regenerateJob`)
16
- - **Reformulate** for a specific audience — owner, referral, specialist (`reformulateReport`)
17
- - **Manage templates** (`listTemplates`, `createTemplate`, `updateTemplate`, `deleteTemplate`)
18
- - **Verify webhooks** with HMAC (`@reqvet/sdk/webhooks`)
11
+ - **Uploader** un enregistrement audio (`uploadAudio`)
12
+ - **Générer** un compte rendu vétérinaire (`createJob`, `generateReport`)
13
+ - **Suivre** les jobs — via webhook ou polling (`getJob`, `waitForJob`, `listJobs`)
14
+ - **Amender** un compte rendu terminé avec un audio complémentaire (`amendJob`)
15
+ - **Régénérer** avec de nouvelles instructions (`regenerateJob`)
16
+ - **Reformuler** pour une audience spécifique propriétaire, référé, spécialiste (`reformulateReport`)
17
+ - **Gérer les templates** (`listTemplates`, `createTemplate`, `updateTemplate`, `deleteTemplate`)
18
+ - **Vérifier les webhooks** avec HMAC (`@reqvet-sdk/sdk/webhooks`)
19
19
 
20
- > **Note**: this SDK does not include an audio recorder. Your application handles recording and passes a `File`, `Blob`, or `Buffer` to the SDK.
20
+ > **Note** : ce SDK n'inclut pas d'enregistreur audio. Votre application gère l'enregistrement et passe un `File`, `Blob` ou `Buffer` au SDK.
21
21
 
22
22
  ## Installation
23
23
 
24
24
  ```bash
25
- npm install @reqvet/sdk
25
+ npm install @reqvet-sdk/sdk
26
26
  ```
27
27
 
28
- Requires Node.js ≥ 18. Works in modern browsers for client methods (Blob/FormData required for upload).
28
+ Nécessite Node.js ≥ 18. Fonctionne dans les navigateurs modernes pour les méthodes client (Blob/FormData requis pour l'upload).
29
29
 
30
- ## Before your first call
30
+ ## Avant votre premier appel
31
31
 
32
- Your ReqVet account manager will provide three environment variables:
32
+ Votre responsable de compte ReqVet vous fournira trois variables d'environnement :
33
33
 
34
34
  ```bash
35
35
  REQVET_API_KEY=rqv_live_...
36
36
  REQVET_BASE_URL=https://api.reqvet.com
37
- REQVET_WEBHOOK_SECRET=... # only needed if using webhooks
37
+ REQVET_WEBHOOK_SECRET=... # uniquement si vous utilisez les webhooks
38
38
  ```
39
39
 
40
- Every job requires a `templateId`. **Call `listTemplates()` first** to discover what's available:
40
+ Chaque job nécessite un `templateId`. **Appelez `listTemplates()` en premier** pour découvrir ce qui est disponible :
41
41
 
42
42
  ```ts
43
43
  const { system, custom } = await reqvet.listTemplates();
44
- // system = ReqVet-provided templates, visible to all organizations (read-only)
45
- // custom = templates created by your organization
44
+ // system = templates fournis par ReqVet, visibles par toutes les organisations (lecture seule)
45
+ // custom = templates créés par votre organisation
46
46
 
47
47
  const templateId = system[0].id;
48
48
  ```
49
49
 
50
- ## Quick start
50
+ ## Démarrage rapide
51
51
 
52
- ### Webhook flow (recommended)
52
+ ### Flux webhook (recommandé)
53
+
54
+ Pour les intégrations serveur (Next.js, Express…), utilisez `getSignedUploadUrl()` qui uploade le fichier directement dans Supabase sans passer par une Vercel Serverless Function — **pas de limite de taille**.
53
55
 
54
56
  ```ts
55
- import ReqVet from '@reqvet/sdk';
57
+ import ReqVet from '@reqvet-sdk/sdk';
56
58
 
57
59
  const reqvet = new ReqVet(process.env.REQVET_API_KEY!, {
58
60
  baseUrl: process.env.REQVET_BASE_URL,
59
61
  });
60
62
 
61
- // 1. Upload the audio
62
- const { path } = await reqvet.uploadAudio(audioBuffer, 'consultation.webm');
63
+ // 1. Obtenir une URL signée Supabase (requête JSON légère, pas de fichier)
64
+ const { uploadUrl, path } = await reqvet.getSignedUploadUrl(
65
+ 'consultation.webm',
66
+ 'audio/webm',
67
+ );
68
+
69
+ // 2. Uploader directement vers Supabase (contourne Vercel, pas de limite de taille)
70
+ await fetch(uploadUrl, {
71
+ method: 'PUT',
72
+ headers: { 'Content-Type': 'audio/webm' },
73
+ body: audioBuffer, // Buffer | Blob | File
74
+ });
63
75
 
64
- // 2. Create a job — ReqVet will POST the result to your webhook when ready
76
+ // 3. Créer un job — ReqVet POSTera le résultat sur votre webhook quand il sera prêt
65
77
  const job = await reqvet.createJob({
66
78
  audioFile: path,
67
79
  animalName: 'Rex',
68
80
  templateId: 'your-template-uuid',
69
81
  callbackUrl: 'https://your-app.com/api/reqvet/webhook',
70
- metadata: { consultationId: 'abc123' }, // passed through to your webhook
82
+ metadata: { consultationId: 'abc123' },
71
83
  });
72
84
  // { job_id: '...', status: 'pending' }
73
85
  ```
74
86
 
75
- Your webhook receives a `job.completed` event:
87
+ > **`uploadAudio()` vs `getSignedUploadUrl()`**
88
+ >
89
+ > `uploadAudio()` est pratique pour des fichiers légers (< 4 MB) ou des contextes navigateur.
90
+ > Pour les proxies serveur, préférez `getSignedUploadUrl()` : le fichier va directement dans Supabase,
91
+ > sans passer par `/api/v1/upload` (Vercel Serverless Function, limite ~4.5 MB).
92
+
93
+ Votre webhook reçoit un événement `job.completed` :
76
94
 
77
95
  ```json
78
96
  {
@@ -86,7 +104,7 @@ Your webhook receives a `job.completed` event:
86
104
  }
87
105
  ```
88
106
 
89
- ### Polling flow (simpler for development)
107
+ ### Flux polling (plus simple pour le développement)
90
108
 
91
109
  ```ts
92
110
  const report = await reqvet.generateReport({
@@ -99,10 +117,10 @@ const report = await reqvet.generateReport({
99
117
  // { jobId, html, fields, transcription, cost, metadata }
100
118
  ```
101
119
 
102
- ### Verify an incoming webhook
120
+ ### Vérifier un webhook entrant
103
121
 
104
122
  ```ts
105
- import { verifyWebhookSignature } from '@reqvet/sdk/webhooks';
123
+ import { verifyWebhookSignature } from '@reqvet-sdk/sdk/webhooks';
106
124
 
107
125
  export async function POST(req: NextRequest) {
108
126
  const rawBody = await req.text();
@@ -123,36 +141,37 @@ export async function POST(req: NextRequest) {
123
141
 
124
142
  ## API
125
143
 
126
- | Method | Description |
127
- |--------|-------------|
128
- | `uploadAudio(audio, fileName?)` | Upload an audio file |
129
- | `generateReport(params)` | Upload + create job (convenience helper) |
130
- | `createJob(params)` | Create a generation job |
131
- | `listJobs(options?)` | List jobs with pagination and status filter |
132
- | `getJob(jobId)` | Get job status and result |
133
- | `waitForJob(jobId, onStatus?)` | Poll until job completes |
134
- | `regenerateJob(jobId, options?)` | Regenerate a completed report |
135
- | `amendJob(jobId, params)` | Add an audio complement to a completed job |
136
- | `reformulateReport(jobId, params)` | Generate an audience-specific version |
137
- | `listReformulations(jobId)` | List all reformulations for a job |
138
- | `listTemplates()` | List available templates (`{ system, custom }`) |
139
- | `getTemplate(templateId)` | Get a template by ID |
140
- | `createTemplate(params)` | Create a custom template |
141
- | `updateTemplate(templateId, updates)` | Update a template |
142
- | `deleteTemplate(templateId)` | Delete a template |
143
- | `health()` | API health check |
144
-
145
- ## Webhook events
146
-
147
- ReqVet fires 5 event types: `job.completed`, `job.failed`, `job.amended`, `job.amend_failed`, `job.regenerated`.
148
-
149
- Failed deliveries are retried 3 times (0s, 2s, 5s). Implement idempotency in your handler — deduplicate on `job_id + event`.
150
-
151
- See [SDK_REFERENCE.md §6](./SDK_REFERENCE.md#6-webhook-events) for the full payload structure of each event.
144
+ | Méthode | Description |
145
+ |---------|-------------|
146
+ | `getSignedUploadUrl(fileName, contentType)` | URL signée Supabase pour upload direct (recommandé serveur) |
147
+ | `uploadAudio(audio, fileName?)` | Uploader un fichier audio via ReqVet (limite Vercel ~4.5 MB) |
148
+ | `generateReport(params)` | Upload + création de job (helper tout-en-un) |
149
+ | `createJob(params)` | Créer un job de génération |
150
+ | `listJobs(options?)` | Lister les jobs avec pagination et filtre par statut |
151
+ | `getJob(jobId)` | Obtenir le statut et le résultat d'un job |
152
+ | `waitForJob(jobId, onStatus?)` | Attendre en polling la fin d'un job |
153
+ | `regenerateJob(jobId, options?)` | Régénérer un compte rendu terminé |
154
+ | `amendJob(jobId, params)` | Ajouter un audio complémentaire à un job terminé |
155
+ | `reformulateReport(jobId, params)` | Générer une version adaptée à une audience |
156
+ | `listReformulations(jobId)` | Lister toutes les reformulations d'un job |
157
+ | `listTemplates()` | Lister les templates disponibles (`{ system, custom }`) |
158
+ | `getTemplate(templateId)` | Obtenir un template par son ID |
159
+ | `createTemplate(params)` | Créer un template personnalisé |
160
+ | `updateTemplate(templateId, updates)` | Mettre à jour un template |
161
+ | `deleteTemplate(templateId)` | Supprimer un template |
162
+ | `health()` | Vérification de l'état de l'API |
163
+
164
+ ## Événements webhook
165
+
166
+ ReqVet déclenche 5 types d'événements : `job.completed`, `job.failed`, `job.amended`, `job.amend_failed`, `job.regenerated`.
167
+
168
+ Les livraisons échouées sont retentées 3 fois (0s, 2s, 5s). Implémentez l'idempotence dans votre handler — dédoublonnez sur `job_id + event`.
169
+
170
+ Voir [SDK_REFERENCE.md §6](./SDK_REFERENCE.md#6-webhook-events) pour la structure complète des payloads de chaque événement.
152
171
 
153
172
  ## TypeScript
154
173
 
155
- Full TypeScript definitions included:
174
+ Définitions TypeScript complètes incluses :
156
175
 
157
176
  ```ts
158
177
  import type {
@@ -162,18 +181,18 @@ import type {
162
181
  Template,
163
182
  ReqVetReformulation,
164
183
  ExtractedFields,
165
- } from '@reqvet/sdk';
184
+ } from '@reqvet-sdk/sdk';
166
185
  ```
167
186
 
168
- ## Further reading
187
+ ## Pour aller plus loin
169
188
 
170
- - [SDK_REFERENCE.md](./SDK_REFERENCE.md) — full parameter and response documentation, all webhook payloads, field schema, error codes
171
- - [SECURITY.md](./SECURITY.md) — security guidelines, proxy pattern, complete webhook verification example
189
+ - [SDK_REFERENCE.md](./SDK_REFERENCE.md) — documentation complète des paramètres et réponses, tous les payloads webhook, schéma des champs, codes d'erreur
190
+ - [SECURITY.md](./SECURITY.md) — bonnes pratiques de sécurité, pattern proxy, exemple complet de vérification webhook
172
191
 
173
- ## Security
192
+ ## Sécurité
174
193
 
175
- **Never** expose your API key in client-side code. Always use the SDK server-side and proxy requests from your frontend. See [SECURITY.md](./SECURITY.md).
194
+ **Ne jamais** exposer votre clé API dans du code côté client. Utilisez toujours le SDK côté serveur et proxifiez les requêtes depuis votre frontend. Voir [SECURITY.md](./SECURITY.md).
176
195
 
177
- ## License
196
+ ## Licence
178
197
 
179
198
  MIT
package/SDK_REFERENCE.md CHANGED
@@ -1,64 +1,65 @@
1
- # @reqvet/sdk — Technical Reference
1
+ # @reqvet-sdk/sdk — Référence technique
2
2
 
3
- Complete parameter and response documentation for all SDK methods.
3
+ Documentation complète des paramètres et réponses pour toutes les méthodes du SDK.
4
4
 
5
5
  ---
6
6
 
7
- ## 1) Instantiation
7
+ ## 1) Instanciation
8
8
 
9
9
  ```ts
10
- import ReqVet from '@reqvet/sdk';
10
+ import ReqVet from '@reqvet-sdk/sdk';
11
11
 
12
12
  const reqvet = new ReqVet(process.env.REQVET_API_KEY!, {
13
13
  baseUrl: process.env.REQVET_BASE_URL ?? 'https://api.reqvet.com',
14
- pollInterval: 5000, // polling interval in ms (default: 5000)
15
- timeout: 5 * 60 * 1000, // max polling wait in ms (default: 300 000 = 5 min)
14
+ pollInterval: 5000, // intervalle de polling en ms (défaut : 5000)
15
+ timeout: 5 * 60 * 1000, // attente maximale en polling en ms (défaut : 300 000 = 5 min)
16
16
  });
17
17
  ```
18
18
 
19
- The API key must start with `rqv_`. An `Error` is thrown immediately if it doesn't.
19
+ La clé API doit commencer par `rqv_`. Une `Error` est levée immédiatement dans le cas contraire.
20
20
 
21
21
  ---
22
22
 
23
- ## 2) Before your first call
23
+ ## 2) Avant votre premier appel
24
24
 
25
- ### Get your credentials
25
+ ### Obtenir vos identifiants
26
26
 
27
- Your ReqVet account manager will provide:
28
- - `REQVET_API_KEY` — your org API key (`rqv_live_...`)
29
- - `REQVET_BASE_URL` — the API base URL
30
- - `REQVET_WEBHOOK_SECRET` — your webhook signing secret (if using webhooks)
27
+ Votre responsable de compte ReqVet vous fournira :
31
28
 
32
- ### Discover your templates
29
+ - `REQVET_API_KEY` votre clé API d'organisation (`rqv_live_...`)
30
+ - `REQVET_BASE_URL` — l'URL de base de l'API
31
+ - `REQVET_WEBHOOK_SECRET` — votre secret de signature webhook (si vous utilisez les webhooks)
33
32
 
34
- Every job requires a `templateId`. Before generating reports, list the templates available to your organization:
33
+ ### Découvrir vos templates
34
+
35
+ Chaque job nécessite un `templateId`. Avant de générer des comptes rendus, listez les templates disponibles pour votre organisation :
35
36
 
36
37
  ```ts
37
38
  const { custom, system } = await reqvet.listTemplates();
38
- // system = templates created by ReqVet, available to all organizations (read-only)
39
- // custom = templates created by your organization
40
- const templateId = system[0].id; // or custom[0].id
39
+ // system = templates créés par ReqVet, disponibles pour toutes les organisations (lecture seule)
40
+ // custom = templates créés par votre organisation
41
+ const templateId = system[0].id; // ou custom[0].id
41
42
  ```
42
43
 
43
44
  ---
44
45
 
45
- ## 3) Integration patterns
46
+ ## 3) Patterns d'intégration
46
47
 
47
- ### A) Webhook-first (recommended for production)
48
+ ### A) Webhook en priorité (recommandé pour la production)
48
49
 
49
50
  ```
50
- uploadAudio() → createJob({ callbackUrl }) → ReqVet POSTs result to your endpoint
51
+ uploadAudio() → createJob({ callbackUrl }) → ReqVet POSTe le résultat sur votre endpoint
51
52
  ```
52
53
 
53
- The user can close the browser the result arrives on your server asynchronously.
54
+ L'utilisateur peut fermer le navigateurle résultat arrive sur votre serveur de manière asynchrone.
54
55
 
55
- ### B) Polling (development / simple integrations)
56
+ ### B) Polling (développement / intégrations simples)
56
57
 
57
58
  ```
58
- uploadAudio() → createJob() → waitForJob() → report
59
+ uploadAudio() → createJob() → waitForJob() → rapport
59
60
  ```
60
61
 
61
- Or use the convenience wrapper:
62
+ Ou utilisez le wrapper pratique :
62
63
 
63
64
  ```ts
64
65
  const report = await reqvet.generateReport({ audio, animalName, templateId, waitForResult: true });
@@ -66,92 +67,143 @@ const report = await reqvet.generateReport({ audio, animalName, templateId, wait
66
67
 
67
68
  ---
68
69
 
69
- ## 4) Methods
70
+ ## 4) Méthodes
71
+
72
+ ### `getSignedUploadUrl(fileName, contentType)` ⭐ recommandé serveur
73
+
74
+ Obtenir une URL signée Supabase pour uploader le fichier audio directement, sans passer par une Vercel Serverless Function.
75
+
76
+ **Quand l'utiliser :** intégrations serveur (Next.js proxy, Express, etc.), fichiers > 4 MB.
77
+
78
+ **Flow :**
79
+ ```
80
+ getSignedUploadUrl() → PUT uploadUrl (Supabase direct) → createJob({ audioFile: path })
81
+ ```
82
+
83
+ **Paramètres :**
84
+ | Nom | Type | Requis | Description |
85
+ |-----|------|--------|-------------|
86
+ | `fileName` | `string` | ✅ | Nom du fichier (ex. `consultation.webm`) |
87
+ | `contentType` | `string` | ✅ | Type MIME (ex. `audio/webm`) |
88
+
89
+ **Réponse :**
90
+ ```ts
91
+ {
92
+ uploadUrl: string; // URL presignée Supabase — à utiliser avec PUT
93
+ path: string; // chemin de stockage — à passer à createJob()
94
+ }
95
+ ```
96
+
97
+ **Exemple :**
98
+ ```ts
99
+ const { uploadUrl, path } = await reqvet.getSignedUploadUrl('consultation.webm', 'audio/webm');
100
+
101
+ await fetch(uploadUrl, {
102
+ method: 'PUT',
103
+ headers: { 'Content-Type': 'audio/webm' },
104
+ body: audioBuffer,
105
+ });
106
+
107
+ const job = await reqvet.createJob({ audioFile: path, animalName, templateId });
108
+ ```
109
+
110
+ ---
70
111
 
71
112
  ### `uploadAudio(audio, fileName?)`
72
113
 
73
- Upload an audio file to ReqVet storage.
114
+ Uploader un fichier audio vers le stockage ReqVet via `/api/v1/upload`.
74
115
 
75
- **Parameters:**
76
- | Name | Type | Required | Description |
77
- |------|------|----------|-------------|
78
- | `audio` | `Blob \| File \| Buffer` | ✅ | Audio data |
79
- | `fileName` | `string` | — | File name, used to infer MIME type (default: `audio.webm`) |
116
+ > ⚠️ **Limite serveur** : `/api/v1/upload` est une Vercel Serverless Function avec une limite de payload de ~4.5 MB. Pour les proxies serveur gérant des fichiers > 4 MB, utilisez [`getSignedUploadUrl()`](#getsigneduploadurlfilename-contenttype--recommandé-serveur) à la place.
117
+ >
118
+ > `uploadAudio()` reste adapté pour les contextes navigateur (Blob/File natif, pas de limite Vercel) ou les fichiers légers.
119
+
120
+ **Paramètres :**
121
+ | Nom | Type | Requis | Description |
122
+ |-----|------|--------|-------------|
123
+ | `audio` | `Blob \| File \| Buffer` | ✅ | Données audio |
124
+ | `fileName` | `string` | — | Nom du fichier, utilisé pour inférer le type MIME (défaut : `audio.webm`) |
125
+
126
+ **Réponse :**
80
127
 
81
- **Response:**
82
128
  ```ts
83
129
  {
84
- audio_file: string; // canonical storage pathpass this to createJob()
85
- path: string; // alias of audio_file
130
+ audio_file: string; // chemin de stockage canonique à passer à createJob()
131
+ path: string; // alias de audio_file
86
132
  size_bytes: number;
87
133
  content_type: string;
88
134
  }
89
135
  ```
90
136
 
91
- Supported formats: `mp3`, `wav`, `webm`, `ogg`, `m4a`, `aac`, `flac`. Max size: 100 MB.
137
+ Formats supportés : `mp3`, `wav`, `webm`, `ogg`, `m4a`, `aac`, `flac`. Taille max : 100 Mo.
92
138
 
93
139
  ---
94
140
 
95
141
  ### `generateReport(params)`
96
142
 
97
- Convenience wrapper: `uploadAudio → createJob`. Optionally waits for completion.
98
-
99
- **Parameters:**
100
- | Name | Type | Required | Description |
101
- |------|------|----------|-------------|
102
- | `audio` | `Blob \| File \| Buffer` | ✅ | Audio data |
103
- | `animalName` | `string` | ✅ | Name of the animal |
104
- | `templateId` | `string` | ✅ | Template UUID (from `listTemplates()`) |
105
- | `fileName` | `string` | — | File name |
106
- | `callbackUrl` | `string` | — | Your webhook endpoint (HTTPS, publicly reachable) |
107
- | `metadata` | `Record<string, unknown>` | — | Passthrough data (e.g. `{ consultationId, vetId }`) |
108
- | `extraInstructions` | `string` | — | Additional generation instructions injected into the prompt |
109
- | `waitForResult` | `boolean` | — | If `true`, polls and returns the final report. Default: `false` |
110
- | `onStatus` | `(status: string) => void` | — | Called on each poll (only when `waitForResult: true`) |
111
-
112
- **Response:**
113
- - `waitForResult: false` (default): `{ job_id: string, status: 'pending' }`
114
- - `waitForResult: true`: `ReqVetReport` (see `waitForJob`)
143
+ Wrapper pratique : `uploadAudio → createJob`. Attend optionnellement la fin du traitement.
144
+
145
+ **Paramètres :**
146
+ | Nom | Type | Requis | Description |
147
+ |-----|------|--------|-------------|
148
+ | `audio` | `Blob \| File \| Buffer` | ✅ | Données audio |
149
+ | `animalName` | `string` | ✅ | Nom de l'animal |
150
+ | `templateId` | `string` | ✅ | UUID du template (depuis `listTemplates()`) |
151
+ | `fileName` | `string` | — | Nom du fichier |
152
+ | `callbackUrl` | `string` | — | Votre endpoint webhook (HTTPS, accessible publiquement) |
153
+ | `metadata` | `Record<string, unknown>` | — | Données passthrough (ex. `{ consultationId, vetId }`) |
154
+ | `extraInstructions` | `string` | — | Instructions de génération supplémentaires injectées dans le prompt |
155
+ | `waitForResult` | `boolean` | — | Si `true`, poll et retourne le rapport final. Défaut : `false` |
156
+ | `onStatus` | `(status: string) => void` | — | Appelé à chaque poll (uniquement si `waitForResult: true`) |
157
+
158
+ **Réponse :**
159
+
160
+ - `waitForResult: false` (défaut) : `{ job_id: string, status: 'pending' }`
161
+ - `waitForResult: true` : `ReqVetReport` (voir `waitForJob`)
115
162
 
116
163
  ---
117
164
 
118
165
  ### `createJob(params)`
119
166
 
120
- Start a transcription + report generation pipeline.
167
+ Démarrer un pipeline de transcription + génération de compte rendu.
168
+
169
+ **Paramètres :**
170
+ | Nom | Type | Requis | Description |
171
+ |-----|------|--------|-------------|
172
+ | `audioFile` | `string` | ✅ | Valeur de `uploadAudio().path` |
173
+ | `animalName` | `string` | ✅ | Nom de l'animal |
174
+ | `templateId` | `string` | ✅ | UUID du template |
175
+ | `callbackUrl` | `string` | — | URL webhook (HTTPS, accessible publiquement). Utilise le webhook par défaut de l'organisation si omis. |
176
+ | `metadata` | `Record<string, unknown>` | — | Données passthrough — pour corréler avec vos propres enregistrements |
177
+ | `extraInstructions` | `string` | — | Instructions de génération supplémentaires (max 5 000 caractères) |
121
178
 
122
- **Parameters:**
123
- | Name | Type | Required | Description |
124
- |------|------|----------|-------------|
125
- | `audioFile` | `string` | ✅ | Value of `uploadAudio().path` |
126
- | `animalName` | `string` | ✅ | Name of the animal |
127
- | `templateId` | `string` | ✅ | Template UUID |
128
- | `callbackUrl` | `string` | — | Webhook URL (HTTPS, publicly reachable). Falls back to the org default webhook if omitted. |
129
- | `metadata` | `Record<string, unknown>` | — | Passthrough data — correlate with your own records |
130
- | `extraInstructions` | `string` | — | Extra generation instructions (max 5 000 chars) |
179
+ **Réponse :**
131
180
 
132
- **Response:**
133
181
  ```ts
134
- { job_id: string; status: 'pending' }
182
+ {
183
+ job_id: string;
184
+ status: 'pending';
185
+ }
135
186
  ```
136
187
 
137
- > **Rate limit**: 10 000 requests/minute per organization.
188
+ > **Limite de débit** : 10 000 requêtes/minute par organisation.
138
189
 
139
190
  ---
140
191
 
141
192
  ### `listJobs(options?)`
142
193
 
143
- List jobs for the authenticated organization, with pagination and filtering.
194
+ Lister les jobs de l'organisation authentifiée, avec pagination et filtrage.
144
195
 
145
- **Parameters:**
146
- | Name | Type | Default | Description |
147
- |------|------|---------|-------------|
148
- | `limit` | `number` | `20` | Results per page (1–100) |
149
- | `offset` | `number` | `0` | Pagination offset |
150
- | `status` | `string` | — | Filter: `pending` `transcribing` `generating` `completed` `failed` `amending` |
151
- | `sort` | `string` | `created_at` | Sort field: `created_at` or `updated_at` |
152
- | `order` | `string` | `desc` | Direction: `asc` or `desc` |
196
+ **Paramètres :**
197
+ | Nom | Type | Défaut | Description |
198
+ |-----|------|--------|-------------|
199
+ | `limit` | `number` | `20` | Résultats par page (1–100) |
200
+ | `offset` | `number` | `0` | Décalage de pagination |
201
+ | `status` | `string` | — | Filtre : `pending` `transcribing` `generating` `completed` `failed` `amending` |
202
+ | `sort` | `string` | `created_at` | Champ de tri : `created_at` ou `updated_at` |
203
+ | `order` | `string` | `desc` | Direction : `asc` ou `desc` |
204
+
205
+ **Réponse :**
153
206
 
154
- **Response:**
155
207
  ```ts
156
208
  {
157
209
  jobs: JobSummary[];
@@ -163,25 +215,26 @@ List jobs for the authenticated organization, with pagination and filtering.
163
215
 
164
216
  ### `getJob(jobId)`
165
217
 
166
- Get the current state and result of a job.
218
+ Obtenir l'état actuel et le résultat d'un job.
219
+
220
+ **Champs de réponse par statut :**
167
221
 
168
- **Response fields by status:**
222
+ | Champ | `pending` | `transcribing` | `generating` | `completed` | `failed` |
223
+ | --------------- | :-------: | :------------: | :----------: | :---------: | :------: |
224
+ | `job_id` | ✅ | ✅ | ✅ | ✅ | ✅ |
225
+ | `status` | ✅ | ✅ | ✅ | ✅ | ✅ |
226
+ | `animal_name` | ✅ | ✅ | ✅ | ✅ | ✅ |
227
+ | `metadata` | ✅ | ✅ | ✅ | ✅ | ✅ |
228
+ | `transcription` | — | — | ✅ | ✅ | — |
229
+ | `result.html` | — | — | — | ✅ | — |
230
+ | `result.fields` | — | — | — | ✅\* | — |
231
+ | `cost` | — | — | — | ✅ | — |
232
+ | `error` | — | — | — | — | ✅ |
169
233
 
170
- | Field | `pending` | `transcribing` | `generating` | `completed` | `failed` |
171
- |-------|:---------:|:--------------:|:------------:|:-----------:|:--------:|
172
- | `job_id` | ✅ | ✅ | ✅ | ✅ | ✅ |
173
- | `status` | ✅ | ✅ | ✅ | ✅ | ✅ |
174
- | `animal_name` | ✅ | ✅ | ✅ | ✅ | ✅ |
175
- | `metadata` | ✅ | ✅ | ✅ | ✅ | ✅ |
176
- | `transcription` | — | — | ✅ | ✅ | — |
177
- | `result.html` | — | — | — | ✅ | — |
178
- | `result.fields` | — | — | — | ✅* | — |
179
- | `cost` | — | — | — | ✅ | — |
180
- | `error` | — | — | — | — | ✅ |
234
+ \*`result.fields` n'est présent que si votre organisation a un `field_schema` configuré (extraction de données structurées). Vaut `null` sinon. Voir [Schéma de champs](#5-schéma-de-champs) ci-dessous.
181
235
 
182
- *`result.fields` is only present if your organization has a `field_schema` configured (structured data extraction). It is `null` otherwise. See [Field schema](#field-schema) below.
236
+ **Structure du coût (jobs terminés) :**
183
237
 
184
- **Cost structure (completed jobs):**
185
238
  ```ts
186
239
  cost: {
187
240
  transcription_usd: number;
@@ -190,91 +243,104 @@ cost: {
190
243
  }
191
244
  ```
192
245
 
193
- > **Note**: `cost` is available via `getJob()` and `waitForJob()`, but is **not** included in webhook payloads. Retrieve it with `getJob()` after receiving a `job.completed` event if needed.
246
+ > **Note** : `cost` est disponible via `getJob()` et `waitForJob()`, mais n'est **pas** inclus dans les payloads webhook. Récupérez-le avec `getJob()` après réception d'un événement `job.completed` si nécessaire.
194
247
 
195
248
  ---
196
249
 
197
250
  ### `waitForJob(jobId, onStatus?)`
198
251
 
199
- Poll until a job reaches `completed` or `failed`. Respects `pollInterval` and `timeout`.
252
+ Poller jusqu'à ce qu'un job atteigne `completed` ou `failed`. Respecte `pollInterval` et `timeout`.
253
+
254
+ **Réponse (`ReqVetReport`) :**
200
255
 
201
- **Response (`ReqVetReport`):**
202
256
  ```ts
203
257
  {
204
258
  jobId: string;
205
- html: string; // generated report HTML
206
- fields: ExtractedFields | null; // null if no field_schema configured
259
+ html: string; // HTML du compte rendu généré
260
+ fields: ExtractedFields | null; // null si aucun field_schema configuré
207
261
  transcription: string;
208
262
  animalName: string;
209
- cost: { transcription_usd: number; generation_usd: number; total_usd: number };
263
+ cost: {
264
+ transcription_usd: number;
265
+ generation_usd: number;
266
+ total_usd: number;
267
+ }
210
268
  metadata: Record<string, unknown>;
211
269
  }
212
270
  ```
213
271
 
214
- Throws `ReqVetError` if the job fails or the timeout is exceeded.
272
+ Lève une `ReqVetError` si le job échoue ou si le timeout est dépassé.
215
273
 
216
274
  ---
217
275
 
218
276
  ### `regenerateJob(jobId, options?)`
219
277
 
220
- Regenerate the report for a completed job — e.g. with different instructions or a different template.
278
+ Régénérer le compte rendu d'un job terminé par exemple avec des instructions différentes ou un autre template.
279
+
280
+ **Paramètres :**
281
+ | Nom | Type | Description |
282
+ |-----|------|-------------|
283
+ | `extraInstructions` | `string` | Nouvelles instructions (max 2 000 caractères) |
284
+ | `templateId` | `string` | Basculer vers un autre template |
221
285
 
222
- **Parameters:**
223
- | Name | Type | Description |
224
- |------|------|-------------|
225
- | `extraInstructions` | `string` | New instructions (max 2 000 chars) |
226
- | `templateId` | `string` | Switch to a different template |
286
+ **Réponse :**
227
287
 
228
- **Response:**
229
288
  ```ts
230
289
  { job_id: string; status: 'completed'; result: { html: string; fields?: ExtractedFields } }
231
290
  ```
232
291
 
233
- Triggers a `job.regenerated` webhook event if a `callbackUrl` is configured.
292
+ Déclenche un événement webhook `job.regenerated` si un `callbackUrl` est configuré.
234
293
 
235
- > **Rate limit**: 30 requests/minute per organization.
294
+ > **Limite de débit** : 30 requêtes/minute par organisation.
236
295
 
237
296
  ---
238
297
 
239
298
  ### `amendJob(jobId, params)`
240
299
 
241
- Add an audio complement to a completed job. The new audio is transcribed, merged with the existing transcription, and the report is regenerated.
300
+ Ajouter un audio complémentaire à un job terminé. Le nouvel audio est transcrit, fusionné avec la transcription existante, et le compte rendu est régénéré.
242
301
 
243
- **Parameters:**
244
- | Name | Type | Required | Description |
245
- |------|------|----------|-------------|
246
- | `audioFile` | `string` | ✅ | Value of `uploadAudio().path` |
247
- | `templateId` | `string` | — | Switch to a different template |
302
+ **Paramètres :**
303
+ | Nom | Type | Requis | Description |
304
+ |-----|------|--------|-------------|
305
+ | `audioFile` | `string` | ✅ | Valeur de `uploadAudio().path` |
306
+ | `templateId` | `string` | — | Basculer vers un autre template |
307
+
308
+ **Réponse :**
248
309
 
249
- **Response:**
250
310
  ```ts
251
- { job_id: string; status: 'amending'; amendment_number: number; message: string }
311
+ {
312
+ job_id: string;
313
+ status: 'amending';
314
+ amendment_number: number;
315
+ message: string;
316
+ }
252
317
  ```
253
318
 
254
- The job returns to `completed` when the amendment finishes. Use `waitForJob()` or listen for the `job.amended` webhook event. Multiple amendments are supported each one appends to the full transcription.
319
+ Le job repasse à `completed` quand l'amendement est terminé. Utilisez `waitForJob()` ou écoutez l'événement webhook `job.amended`. Plusieurs amendements sont supportéschacun est ajouté à la transcription complète.
255
320
 
256
321
  ---
257
322
 
258
323
  ### `reformulateReport(jobId, params)`
259
324
 
260
- Generate an alternative version of a completed report for a specific audience.
325
+ Générer une version alternative d'un compte rendu terminé pour une audience spécifique.
261
326
 
262
- **Parameters:**
263
- | Name | Type | Required | Description |
264
- |------|------|----------|-------------|
327
+ **Paramètres :**
328
+ | Nom | Type | Requis | Description |
329
+ |-----|------|--------|-------------|
265
330
  | `purpose` | `string` | ✅ | `owner` `referral` `summary` `custom` `diagnostic_hypothesis` |
266
- | `customInstructions` | `string` | If `purpose: 'custom'` | Reformulation instructions |
267
-
268
- **Purpose values:**
269
- | Value | Output |
270
- |-------|--------|
271
- | `owner` | Simplified version for the pet owner |
272
- | `referral` | Clinical summary for a specialist |
273
- | `summary` | Short internal note |
274
- | `diagnostic_hypothesis` | Differential diagnosis list |
275
- | `custom` | Defined by `customInstructions` |
276
-
277
- **Response (`ReqVetReformulation`):**
331
+ | `customInstructions` | `string` | Si `purpose: 'custom'` | Instructions de reformulation |
332
+
333
+ **Valeurs de `purpose` :**
334
+ | Valeur | Résultat |
335
+ |--------|----------|
336
+ | `owner` | Version simplifiée pour le propriétaire de l'animal |
337
+ | `referral` | Résumé clinique pour un spécialiste |
338
+ | `summary` | Note interne courte |
339
+ | `diagnostic_hypothesis` | Liste de diagnostics différentiels |
340
+ | `custom` | Défini par `customInstructions` |
341
+
342
+ **Réponse (`ReqVetReformulation`) :**
343
+
278
344
  ```ts
279
345
  {
280
346
  id: string;
@@ -287,13 +353,13 @@ Generate an alternative version of a completed report for a specific audience.
287
353
  }
288
354
  ```
289
355
 
290
- > **Rate limit**: 30 requests/minute per organization.
356
+ > **Limite de débit** : 30 requêtes/minute par organisation.
291
357
 
292
358
  ---
293
359
 
294
360
  ### `listReformulations(jobId)`
295
361
 
296
- **Response:** `{ reformulations: ReqVetReformulation[] }`
362
+ **Réponse :** `{ reformulations: ReqVetReformulation[] }`
297
363
 
298
364
  ---
299
365
 
@@ -301,23 +367,23 @@ Generate an alternative version of a completed report for a specific audience.
301
367
 
302
368
  #### `listTemplates()` → `{ custom: Template[], system: Template[] }`
303
369
 
304
- - **`system`** — templates created by ReqVet, visible to all organizations. Read-only. Start here to find available `templateId` values.
305
- - **`custom`** — templates created by your organization. Editable via `createTemplate` / `updateTemplate`.
370
+ - **`system`** — templates créés par ReqVet, visibles par toutes les organisations. Lecture seule. Commencez ici pour trouver les `templateId` disponibles.
371
+ - **`custom`** — templates créés par votre organisation. Modifiables via `createTemplate` / `updateTemplate`.
306
372
 
307
373
  #### `getTemplate(templateId)` → `Template`
308
374
 
309
375
  #### `createTemplate(params)` → `Template`
310
376
 
311
- | Name | Type | Required |
312
- |------|------|----------|
313
- | `name` | `string` | ✅ |
314
- | `content` | `string` | ✅ |
315
- | `description` | `string` | — |
316
- | `is_default` | `boolean` | — |
377
+ | Nom | Type | Requis |
378
+ | ------------- | --------- | ------ |
379
+ | `name` | `string` | ✅ |
380
+ | `content` | `string` | ✅ |
381
+ | `description` | `string` | — |
382
+ | `is_default` | `boolean` | — |
317
383
 
318
384
  #### `updateTemplate(templateId, updates)` → `Template`
319
385
 
320
- All fields optional (partial update). Same fields as `createTemplate`.
386
+ Tous les champs sont optionnels (mise à jour partielle). Mêmes champs que `createTemplate`.
321
387
 
322
388
  #### `deleteTemplate(templateId)` → `{ success: true }`
323
389
 
@@ -325,15 +391,15 @@ All fields optional (partial update). Same fields as `createTemplate`.
325
391
 
326
392
  ### `health()`
327
393
 
328
- **Response:** `{ status: 'ok' | 'degraded'; timestamp: string }`
394
+ **Réponse :** `{ status: 'ok' | 'degraded'; timestamp: string }`
329
395
 
330
396
  ---
331
397
 
332
- ## 5) Field schema
398
+ ## 5) Schéma de champs
333
399
 
334
- If your organization has a `field_schema` configured, ReqVet extracts structured fields from each consultation in addition to generating the HTML report.
400
+ Si votre organisation a un `field_schema` configuré, ReqVet extrait des champs structurés de chaque consultation en plus de générer le compte rendu HTML.
335
401
 
336
- Example `result.fields` for a standard checkup:
402
+ Exemple de `result.fields` pour un bilan de santé standard :
337
403
 
338
404
  ```json
339
405
  {
@@ -347,23 +413,23 @@ Example `result.fields` for a standard checkup:
347
413
  }
348
414
  ```
349
415
 
350
- `fields` is `null` if no `field_schema` is configured for your organization. Contact your ReqVet account manager to enable and configure structured extraction.
416
+ `fields` vaut `null` si aucun `field_schema` n'est configuré pour votre organisation. Contactez votre responsable de compte ReqVet pour activer et configurer l'extraction structurée.
351
417
 
352
418
  ---
353
419
 
354
- ## 6) Webhook events
420
+ ## 6) Événements webhook
355
421
 
356
- ReqVet POSTs to your `callbackUrl` when a job changes state. All events share the same format.
422
+ ReqVet POSTe sur votre `callbackUrl` quand un job change d'état. Tous les événements partagent le même format.
357
423
 
358
- ### Headers
424
+ ### En-têtes
359
425
 
360
426
  ```
361
427
  Content-Type: application/json
362
- X-ReqVet-Signature: sha256=<hex> (only if org has a webhook_secret)
363
- X-ReqVet-Timestamp: <unix_ms> (only if org has a webhook_secret)
428
+ X-ReqVet-Signature: sha256=<hex> (uniquement si l'organisation a un webhook_secret)
429
+ X-ReqVet-Timestamp: <unix_ms> (uniquement si l'organisation a un webhook_secret)
364
430
  ```
365
431
 
366
- ### Event types and payloads
432
+ ### Types d'événements et payloads
367
433
 
368
434
  #### `job.completed`
369
435
 
@@ -379,7 +445,7 @@ X-ReqVet-Timestamp: <unix_ms> (only if org has a webhook_secret)
379
445
  }
380
446
  ```
381
447
 
382
- > `fields` is absent if the organization has no `field_schema`. `cost` is not in the webhook — retrieve it with `getJob()` if needed.
448
+ > `fields` est absent si l'organisation n'a pas de `field_schema`. `cost` n'est pas dans le webhook — récupérez-le avec `getJob()` si nécessaire.
383
449
 
384
450
  ---
385
451
 
@@ -399,14 +465,14 @@ X-ReqVet-Timestamp: <unix_ms> (only if org has a webhook_secret)
399
465
 
400
466
  #### `job.amended`
401
467
 
402
- Sent when an amendment (`amendJob`) completes successfully.
468
+ Envoyé quand un amendement (`amendJob`) se termine avec succès.
403
469
 
404
470
  ```json
405
471
  {
406
472
  "event": "job.amended",
407
473
  "job_id": "a1b2c3d4-...",
408
474
  "animal_name": "Rex",
409
- "transcription": "...full transcription including amendment...",
475
+ "transcription": "...transcription complète incluant l'amendement...",
410
476
  "html": "<section class=\"cr\">...</section>",
411
477
  "amendment_number": 1,
412
478
  "fields": { "espece": "Chien", "poids": 28.5 },
@@ -418,7 +484,7 @@ Sent when an amendment (`amendJob`) completes successfully.
418
484
 
419
485
  #### `job.amend_failed`
420
486
 
421
- Sent when amendment transcription fails. The original report is preserved.
487
+ Envoyé quand la transcription d'un amendement échoue. Le compte rendu original est préservé.
422
488
 
423
489
  ```json
424
490
  {
@@ -434,7 +500,7 @@ Sent when amendment transcription fails. The original report is preserved.
434
500
 
435
501
  #### `job.regenerated`
436
502
 
437
- Sent when `regenerateJob()` completes.
503
+ Envoyé quand `regenerateJob()` se termine.
438
504
 
439
505
  ```json
440
506
  {
@@ -449,68 +515,68 @@ Sent when `regenerateJob()` completes.
449
515
 
450
516
  ---
451
517
 
452
- ### Retry policy
518
+ ### Politique de retry
453
519
 
454
- ReqVet retries failed webhook deliveries **3 times** with delays of 0s, 2s, and 5s. After 3 failures, the event is marked as undelivered. Implement idempotency in your handler (deduplicate on `job_id + event`).
520
+ ReqVet retente les livraisons webhook échouées **3 fois** avec des délais de 0s, 2s et 5s. Après 3 échecs, l'événement est marqué comme non livré. Implémentez l'idempotence dans votre handler (dédoublonnez sur `job_id + event`).
455
521
 
456
522
  ---
457
523
 
458
- ## 7) Webhook verification
524
+ ## 7) Vérification des webhooks
459
525
 
460
526
  ```ts
461
- import { verifyWebhookSignature } from '@reqvet/sdk/webhooks';
527
+ import { verifyWebhookSignature } from '@reqvet-sdk/sdk/webhooks';
462
528
 
463
529
  const { ok, reason } = verifyWebhookSignature({
464
530
  secret: process.env.REQVET_WEBHOOK_SECRET!,
465
- rawBody, // raw request body stringread BEFORE JSON.parse
466
- signature, // X-ReqVet-Signature header value
467
- timestamp, // X-ReqVet-Timestamp header value
468
- maxSkewMs: 5 * 60 * 1000, // reject events older than 5 min (default)
531
+ rawBody, // corps brut de la requête à lire AVANT JSON.parse
532
+ signature, // valeur de l'en-tête X-ReqVet-Signature
533
+ timestamp, // valeur de l'en-tête X-ReqVet-Timestamp
534
+ maxSkewMs: 5 * 60 * 1000, // rejeter les événements de plus de 5 min (défaut)
469
535
  });
470
536
  ```
471
537
 
472
- Rejection reasons: `missing_headers` `invalid_timestamp` `stale_timestamp` `invalid_signature`
538
+ Raisons de rejet : `missing_headers` `invalid_timestamp` `stale_timestamp` `invalid_signature`
473
539
 
474
- See [SECURITY.md](./SECURITY.md) for a complete Next.js implementation example.
540
+ Voir [SECURITY.md](./SECURITY.md) pour un exemple d'implémentation complet avec Next.js.
475
541
 
476
542
  ---
477
543
 
478
- ## 8) Error handling
544
+ ## 8) Gestion des erreurs
479
545
 
480
- All methods throw `ReqVetError` on HTTP errors or network failures:
546
+ Toutes les méthodes lèvent une `ReqVetError` en cas d'erreur HTTP ou de panne réseau :
481
547
 
482
548
  ```ts
483
- import { ReqVetError } from '@reqvet/sdk';
549
+ import { ReqVetError } from '@reqvet-sdk/sdk';
484
550
 
485
551
  try {
486
552
  const report = await reqvet.waitForJob(jobId);
487
553
  } catch (err) {
488
554
  if (err instanceof ReqVetError) {
489
- console.error(err.message); // human-readable message
490
- console.error(err.status); // HTTP status (0 for network/timeout errors)
491
- console.error(err.body); // raw response body
555
+ console.error(err.message); // message lisible par un humain
556
+ console.error(err.status); // statut HTTP (0 pour les erreurs réseau/timeout)
557
+ console.error(err.body); // corps brut de la réponse
492
558
  }
493
559
  }
494
560
  ```
495
561
 
496
- | Status | Meaning |
497
- |--------|---------|
498
- | `400` | Validation errorcheck `err.body.issues` |
499
- | `401` | Invalid or missing API key |
500
- | `403` | Monthly quota exceeded |
501
- | `404` | Job or template not found |
502
- | `429` | Rate limit exceededback off and retry |
503
- | `500` | ReqVet internal error |
562
+ | Statut | Signification |
563
+ | ------ | ------------------------------------------------- |
564
+ | `400` | Erreur de validation vérifiez `err.body.issues` |
565
+ | `401` | Clé API invalide ou manquante |
566
+ | `403` | Quota mensuel dépassé |
567
+ | `404` | Job ou template introuvable |
568
+ | `429` | Limite de débit dépassée attendez et réessayez |
569
+ | `500` | Erreur interne ReqVet |
504
570
 
505
571
  ---
506
572
 
507
- ## 9) Integration checklist
573
+ ## 9) Checklist d'intégration
508
574
 
509
- - [ ] SDK used **server-side only** — API key never in browser bundles
510
- - [ ] `listTemplates()` called at startup to discover available `templateId` values
511
- - [ ] `metadata` used to correlate ReqVet jobs with your own records (`consultationId`, `vetId`, etc.)
512
- - [ ] Webhook endpoint handles all 5 event types: `job.completed`, `job.failed`, `job.amended`, `job.amend_failed`, `job.regenerated`
513
- - [ ] Webhook signature verified on every incoming event
514
- - [ ] Timestamp anti-replay check enabled (`maxSkewMs`)
515
- - [ ] Idempotency implementeddeduplicate on `job_id + event`
516
- - [ ] `REQVET_API_KEY` and `REQVET_WEBHOOK_SECRET` stored in environment variables, never hardcoded
575
+ - [ ] SDK utilisé **côté serveur uniquement** — clé API jamais dans les bundles navigateur
576
+ - [ ] `listTemplates()` appelé au démarrage pour découvrir les `templateId` disponibles
577
+ - [ ] `metadata` utilisé pour corréler les jobs ReqVet avec vos propres enregistrements (`consultationId`, `vetId`, etc.)
578
+ - [ ] L'endpoint webhook gère les 5 types d'événements : `job.completed`, `job.failed`, `job.amended`, `job.amend_failed`, `job.regenerated`
579
+ - [ ] Signature webhook vérifiée sur chaque événement entrant
580
+ - [ ] Vérification anti-replay du timestamp activée (`maxSkewMs`)
581
+ - [ ] Idempotence implémentéedédoublonnage sur `job_id + event`
582
+ - [ ] `REQVET_API_KEY` et `REQVET_WEBHOOK_SECRET` stockés dans des variables d'environnement, jamais en dur dans le code
package/SECURITY.md CHANGED
@@ -29,7 +29,7 @@ Example proxy route (Next.js App Router):
29
29
  ```ts
30
30
  // app/api/reqvet/generate/route.ts
31
31
  import { NextRequest, NextResponse } from 'next/server';
32
- import ReqVet from '@reqvet/sdk';
32
+ import ReqVet from '@reqvet-sdk/sdk';
33
33
 
34
34
  const reqvet = new ReqVet(process.env.REQVET_API_KEY!);
35
35
 
@@ -37,7 +37,21 @@ export async function POST(req: NextRequest) {
37
37
  const form = await req.formData();
38
38
  const audio = form.get('audio') as File;
39
39
 
40
- const { path } = await reqvet.uploadAudio(audio, audio.name);
40
+ // Use getSignedUploadUrl() instead of uploadAudio() for server-side proxies.
41
+ // uploadAudio() posts to /api/v1/upload (Vercel Serverless Function, ~4.5 MB limit).
42
+ // getSignedUploadUrl() uploads directly to Supabase — no size limit.
43
+ const { uploadUrl, path } = await reqvet.getSignedUploadUrl(
44
+ audio.name || 'consultation.webm',
45
+ audio.type || 'audio/webm',
46
+ );
47
+
48
+ const audioBuffer = Buffer.from(await audio.arrayBuffer());
49
+ await fetch(uploadUrl, {
50
+ method: 'PUT',
51
+ headers: { 'Content-Type': audio.type || 'audio/webm' },
52
+ body: audioBuffer,
53
+ });
54
+
41
55
  const job = await reqvet.createJob({
42
56
  audioFile: path,
43
57
  animalName: form.get('animalName') as string,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reqvet-sdk/sdk",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Official JavaScript SDK for the ReqVet veterinary report generation API.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -18,7 +18,13 @@
18
18
  },
19
19
  "./package.json": "./package.json"
20
20
  },
21
- "files": ["src/", "README.md", "SDK_REFERENCE.md", "CHANGELOG.md", "SECURITY.md"],
21
+ "files": [
22
+ "src/",
23
+ "README.md",
24
+ "SDK_REFERENCE.md",
25
+ "CHANGELOG.md",
26
+ "SECURITY.md"
27
+ ],
22
28
  "keywords": [
23
29
  "reqvet",
24
30
  "veterinary",
package/src/index.d.ts CHANGED
@@ -15,6 +15,13 @@ export interface UploadResult {
15
15
  content_type: string;
16
16
  }
17
17
 
18
+ export interface SignedUploadResult {
19
+ /** Presigned URL for direct PUT upload to Supabase storage */
20
+ uploadUrl: string;
21
+ /** Storage path to pass to createJob({ audioFile: path }) */
22
+ path: string;
23
+ }
24
+
18
25
  export interface JobResult {
19
26
  job_id: string;
20
27
  status: 'pending' | 'transcribing' | 'generating' | 'completed' | 'failed' | 'amending';
@@ -201,7 +208,18 @@ export declare class ReqVet {
201
208
  generateReport(params: GenerateReportParams & { waitForResult?: false }): Promise<JobResult>;
202
209
  generateReport(params: GenerateReportParams): Promise<JobResult | ReqVetReport>;
203
210
 
204
- /** Upload an audio file */
211
+ /**
212
+ * Get a presigned URL for direct upload to Supabase storage.
213
+ * Recommended for server-side proxies — bypasses Vercel's ~4.5 MB payload limit.
214
+ * PUT the audio buffer to uploadUrl, then pass path to createJob().
215
+ */
216
+ getSignedUploadUrl(fileName: string, contentType: string): Promise<SignedUploadResult>;
217
+
218
+ /**
219
+ * Upload an audio file via ReqVet's /api/v1/upload endpoint.
220
+ * ⚠️ Subject to Vercel Serverless Function payload limit (~4.5 MB).
221
+ * For files > 4 MB in server-side contexts, use getSignedUploadUrl() instead.
222
+ */
205
223
  uploadAudio(audio: Blob | Buffer | File, fileName?: string): Promise<UploadResult>;
206
224
 
207
225
  /** Create a generation job */
package/src/index.js CHANGED
@@ -108,9 +108,37 @@ class ReqVet {
108
108
 
109
109
  // ─── Upload ────────────────────────────────────────────────
110
110
 
111
+ /**
112
+ * Get a presigned upload URL for direct upload to ReqVet storage (Supabase).
113
+ *
114
+ * Recommended for server-side integrations (e.g. Next.js proxy routes).
115
+ * The file goes directly to Supabase — it never passes through a Vercel
116
+ * Serverless Function, so there is no ~4.5 MB payload limit.
117
+ *
118
+ * Flow:
119
+ * 1. getSignedUploadUrl(fileName, contentType) — tiny JSON request, no file.
120
+ * 2. PUT the audio buffer to uploadUrl (direct to Supabase).
121
+ * 3. Pass path to createJob({ audioFile: path }).
122
+ *
123
+ * @param {string} fileName - File name (e.g. 'consultation.webm')
124
+ * @param {string} contentType - MIME type (e.g. 'audio/webm')
125
+ * @returns {Promise<{uploadUrl: string, path: string}>}
126
+ */
127
+ async getSignedUploadUrl(fileName, contentType) {
128
+ return this._fetch('POST', '/api/v1/storage/signed-upload', {
129
+ file_name: fileName,
130
+ content_type: contentType,
131
+ });
132
+ }
133
+
111
134
  /**
112
135
  * Upload an audio file to ReqVet storage.
113
136
  *
137
+ * ⚠️ This method POSTs the file to /api/v1/upload, which runs as a
138
+ * Vercel Serverless Function (~4.5 MB request limit). For server-side
139
+ * proxies (Next.js, Express…) handling files > 4 MB, prefer
140
+ * getSignedUploadUrl() + a direct PUT to avoid this limit.
141
+ *
114
142
  * @param {Blob|Buffer|File} audio - The audio file
115
143
  * @param {string} [fileName] - File name
116
144
  * @returns {Promise<{audio_file: string, size_bytes: number}>}