Hushscript répond aussi à de simples appels REST, pour les agents et les scripts qui préfèrent appeler un point de terminaison HTTP plutôt que faire tourner un client MCP. C’est le même compte, le même solde et les mêmes jetons porteurs du début à la fin : rien ici ne change la façon dont la transcription est facturée ou stockée.
Démarrage rapide
L’API nécessite un jeton d’accès personnel, et il existe deux façons d’en
détenir un. Une personne en crée un sous Tokens sur la
page MCP du compte, choisit ses
scopes, et décide s’il peut acheter des minutes. Ou un agent ouvre son propre
compte sans aucune personne impliquée : POST /v1/agent/accounts démarre une
inscription payante, et POST /v1/agent/accounts/{signup_id}/claim échange
son secret de réclamation à usage unique contre un compte réel et un premier
jeton. Dans les deux cas, le jeton est affiché une seule fois et ressemble à
hsr1:<region>:pat.<id>.<secret>. Un jeton émis via la connexion OAuth du
serveur MCP fonctionne aussi ici ; voir Se connecter. Un jeton
ne peut jamais créer, lister ni révoquer d’autres jetons, et un compte
détient au maximum 25 jetons actifs ; le déroulé complet du compte d’agent,
rotation d’identifiant comprise, est le guide
Usage automatisé.
Une fois que vous détenez un jeton, appelez l’API directement :
curl https://api.hushscript.com/v1/account \
-H "Authorization: Bearer hsr1:eu:pat.7f3a9c.2b6e1d4a9f2c88b1a3d4e5f60718293"
{
"balance_seconds": 0,
"held_seconds": 0,
"credits_expire_at": null,
"card_on_file": false,
"data_region": "eu",
"limits": {
"max_active_jobs": 3,
"max_daily_seconds": 36000,
"active_jobs": 0,
"daily_seconds_used": 0
}
}
Cela répond avec le solde en secondes, les secondes bloquées par les jobs en
cours, la date d’expiration des crédits, si une carte est enregistrée, la
région de données, et les limites d’admission (max_active_jobs,
max_daily_seconds, et ce qui est déjà utilisé). Un compte tout neuf démarre
avec un solde nul. POST /v1/uploads vérifie le solde, pas la carte : sans
minutes, il répond 3001 insufficient_balance. La carte est ce qui alimente
le solde, puisque la vérification de carte ponctuelle dans l’application
débloque les 30 minutes gratuites, et que les packs s’achètent contre une
carte enregistrée.
Automatiser de bout en bout
Tout ce qui suit l’existence du compte fonctionne sans personne, que ce
compte ait été ouvert dans l’application par une personne ou par un agent via
/v1/agent/accounts (voir Usage automatisé). Une seule étape
de navigateur subsiste, et un agent peut piloter ce navigateur lui-même.
-
Lire le compte.
GET /v1/account. Sicard_on_filevaut false, enregistrez d’abord une carte. -
Enregistrer une carte, une fois.
POST /v1/billing/setup-sessionrenvoie uncheckout_url, unsession_idet unexpires_at:curl -X POST https://api.hushscript.com/v1/billing/setup-session \ -H "Authorization: Bearer hsr1:eu:pat.7f3a9c.2b6e1d4a9f2c88b1a3d4e5f60718293"{ "checkout_url": "https://checkout.stripe.com/c/pay/cs_live_a1B2c3D4e5F6g7H8", "session_id": "seti_1PxYzQ2eZvKYlo2C", "expires_at": "2026-09-11T15:45:00Z" }Ouvrez l’URL dans un navigateur que vous contrôlez et complétez la page Stripe hébergée ; rien n’est débité et Hushscript ne voit jamais la carte. Ensuite,
GET /v1/billing/cardsla liste avec son id.POST /v1/billing/cards/defaultchange la carte par défaut si le compte en détient plusieurs. -
Acheter des minutes.
GET /v1/billing/packsliste les cinq packs avecid,seconds,amountetcurrency:curl https://api.hushscript.com/v1/billing/packs \ -H "Authorization: Bearer hsr1:eu:pat.7f3a9c.2b6e1d4a9f2c88b1a3d4e5f60718293"POST /v1/billing/purchaseavecpack,quick_payment_method_id(l’id de la carte enregistrée) et unidempotency_keydébite la carte hors session et répond avec le nouveaubalance_seconds:curl -X POST https://api.hushscript.com/v1/billing/purchase \ -H "Authorization: Bearer hsr1:eu:pat.7f3a9c.2b6e1d4a9f2c88b1a3d4e5f60718293" \ -H "Content-Type: application/json" \ -d '{ "pack": "300min", "quick_payment_method_id": "pm_2QeT4bY7uX9mK3nP", "idempotency_key": "purchase-2026-09-11-02" }'{ "balance_seconds": 18000 }Cela nécessite le scope
billingsur le jeton et la facturation activée pour le compte. Un corps sans id de carte, ou une carte refusée, est rejeté avec4021 pat_purchase_requires_app; une carte que l’émetteur veut faire vérifier est rejetée avec4023 pat_purchase_requires_authentication. Ni l’un ni l’autre n’ouvre une page que personne ne peut terminer. -
Déposer.
POST /v1/uploadsavecsize_bytes,duration_seconds(de 15 secondes à 10 heures), untitleoptionnel,transcription_optionset unidempotency_key:curl -X POST https://api.hushscript.com/v1/uploads \ -H "Authorization: Bearer hsr1:eu:pat.7f3a9c.2b6e1d4a9f2c88b1a3d4e5f60718293" \ -H "Content-Type: application/json" \ -d '{ "size_bytes": 41943040, "duration_seconds": 1200, "title": "Q3 board call", "transcription_options": { "speaker_detection": true }, "idempotency_key": "upload-2026-09-11-02" }'Cela pose un blocage de crédit et répond avec un
job_idplus une première page d’URL de parties présignées :{ "job_id": "job_5e2a91cf4d7b6081a9f3c2e4b5d6a7c8", "part_urls": [ { "n": 1, "url": "https://r2.hushscript.com/uploads/job_5e2a91cf.../part-1?X-Amz-Signature=..." } ] }Faites un
PUTde chaque partie vers son URL (parties de 32 MiB par défaut, la dernière plus petite) :curl -X PUT "https://r2.hushscript.com/uploads/job_5e2a91cf.../part-1?X-Amz-Signature=..." \ --data-binary @part-01.binRécupérez les pages suivantes via
GET /v1/uploads/{job}/part-urls?start=<n>, et appelezPOST /v1/uploads/{job}/heartbeatbien avant le délai d’inactivité de 20 minutes lors d’un transfert long. Quand les URL présignées sont indisponibles, ou qu’une partie échoue,PUT /v1/uploads/{job}/parts/{n}envoie plutôt les octets de cette partie via l’API ; unPUTrépété remplace la partie, donc les nouvelles tentatives sont sûres. Audio uniquement : mp3, m4a, wav, flac, ogg ou opus, 5 Go au maximum, vérifié côté serveur ; extrayez la piste audio d’une vidéo avant de la déposer. -
Démarrer et attendre.
POST /v1/uploads/{job}/completeavec lenet l’etagde chaque partie envoyée à une URL présignée (omettezpartssi toutes les parties sont passées par l’API) :curl -X POST https://api.hushscript.com/v1/uploads/job_5e2a91cf4d7b6081a9f3c2e4b5d6a7c8/complete \ -H "Authorization: Bearer hsr1:eu:pat.7f3a9c.2b6e1d4a9f2c88b1a3d4e5f60718293" \ -H "Content-Type: application/json" \ -d '{ "parts": [ { "n": 1, "etag": "\"9f86d081884c7d65\"" } ] }'Ensuite, interrogez
GET /v1/jobs/{id}:statepasse paruploading,queuedetprocessingjusqu’àdoneoufailed, et un job terminé porte untranscript_id:curl https://api.hushscript.com/v1/jobs/job_5e2a91cf4d7b6081a9f3c2e4b5d6a7c8 \ -H "Authorization: Bearer hsr1:eu:pat.7f3a9c.2b6e1d4a9f2c88b1a3d4e5f60718293"{ "id": "job_5e2a91cf4d7b6081a9f3c2e4b5d6a7c8", "state": "done", "transcript_id": "trs_2b6e1d4a9f7c3088a1b2c3d4e5f6a7b9" } -
Lire le résultat.
GET /v1/transcripts/{id}renvoie les métadonnées et le corps complet, avec les noms des locuteurs, la langue détectée, les étiquettes et la date de suppression automatique :curl https://api.hushscript.com/v1/transcripts/trs_2b6e1d4a9f7c3088a1b2c3d4e5f6a7b9 \ -H "Authorization: Bearer hsr1:eu:pat.7f3a9c.2b6e1d4a9f2c88b1a3d4e5f60718293"GET /v1/transcriptspagine la liste.
Les options de transcription sont celles de l’application, à l’identique :
language_code ou détection automatique, speaker_detection, medical_mode
(nécessite language_code en en, es, fr ou de, facturé à +100 %),
translation_targets (+25 % par langue), multichannel, keyterms_prompt,
un prompt en texte libre, et les dictionary_ids et prompt_preset_id
enregistrés que liste GET /v1/transcription-context.
Une partie du compte reste MCP uniquement. Importer depuis un lien, modifier
ou supprimer des transcriptions, les Insights, les exports, l’enregistrement
de dictionnaires et de modèles d’invite, et les paramètres du compte n’ont
pas encore de route /v1/ ; le serveur MCP couvre tout cela avec le
même jeton.
Authentification
Chaque appel /v1/ porte un jeton porteur dans l’en-tête Authorization,
vérifié contre les mêmes scopes que ceux qu’utilise le serveur MCP :
| Scope | Ce que ça permet |
|---|---|
transcripts:read |
Lire et exporter les transcriptions, leurs métadonnées et la liste des éléments supprimés |
transcripts:write |
Modifier, étiqueter, traduire, restaurer et supprimer des transcriptions |
transcribe |
Déposer des enregistrements, importer depuis un lien et lancer des transcriptions |
insights |
Lire, générer et supprimer des Insights |
context |
Gérer les dictionnaires enregistrés et les modèles d’invite |
account:read |
Lire le solde, l’utilisation, les transactions et les blocages |
settings:write |
Modifier les paramètres du compte |
org:read |
Lire les membres de l’espace de travail, l’activité de la réserve, les factures et le journal d’audit |
export |
Demander un export complet du compte, vérifier son statut et le télécharger |
billing |
Enregistrer une carte et acheter des packs de minutes |
Un appel hors des scopes du jeton échoue avec pat_scope_missing, en 403. Le
scope billing reste aussi inerte tant que le titulaire du compte n’a pas
activé la facturation pour le compte dans l’application ; un appel de
facturation sur un compte où cet interrupteur est désactivé échoue avec le
même pat_scope_missing, et réémettre le jeton ne le débloque pas. Un jeton
ne peut pas créer, lister ni révoquer de jetons, et un compte détient au
maximum 25 jetons actifs.
Points de terminaison
En service aujourd’hui, chacun protégé par le scope indiqué :
| Méthode | Chemin | Scope |
|---|---|---|
POST |
/v1/agent/accounts |
none |
POST |
/v1/agent/accounts/{signup_id}/claim |
none |
POST |
/v1/agent/credentials/rotate |
none |
GET |
/v1/account |
account:read |
POST |
/v1/billing/setup-session |
billing |
GET |
/v1/billing/cards |
billing |
POST |
/v1/billing/cards/default |
billing |
GET |
/v1/billing/packs |
billing |
POST |
/v1/billing/purchase |
billing |
GET |
/v1/billing/transactions |
account:read |
GET |
/v1/billing/ledger |
account:read |
GET |
/v1/billing/holds |
account:read |
POST |
/v1/uploads |
transcribe |
GET |
/v1/uploads/{job}/part-urls |
transcribe |
PUT |
/v1/uploads/{job}/parts/{n} |
transcribe |
POST |
/v1/uploads/{job}/heartbeat |
transcribe |
POST |
/v1/uploads/{job}/complete |
transcribe |
GET |
/v1/jobs |
transcribe |
GET |
/v1/jobs/{id} |
transcribe |
GET |
/v1/transcripts |
transcripts:read |
GET |
/v1/transcripts/deleted |
transcripts:read |
GET |
/v1/transcripts/{id} |
transcripts:read |
GET |
/v1/transcription-context |
context |
GET |
/v1/org/members |
org:read |
GET |
/v1/org/invoices |
org:read |
GET |
/v1/org/ledger |
org:read |
GET |
/v1/org/audit |
org:read |
/v1/agent/accounts ouvre une inscription sans interface et sans jeton pour
l’instant, /v1/agent/accounts/{signup_id}/claim transforme une inscription
payée en un compte réel et son premier jeton, et
/v1/agent/credentials/rotate génère un successeur pour le jeton appelant et
le révoque, sans toucher à aucun autre identifiant du compte. Le guide
Usage automatisé couvre les trois de bout en bout.
Chaque liste accepte un limit et un cursor opaque, et répond avec un
next_cursor qui vaut null sur la dernière page. Renvoyez le cursor
inchangé ; il encode la position, si bien qu’une page n’est jamais sautée ni
répétée quand des lignes arrivent entre deux appels.
La définition complète et à jour, avec les paramètres et les schémas de réponse de chaque opération, est le document OpenAPI : traitez-le comme la source de vérité plutôt que ce tableau. Il est servi à tout jeton porteur quel que soit son scope, si bien qu’un client peut le récupérer avec les identifiants qu’il détient déjà.
Interface MCP
Vous préférez des outils au HTTP brut ? Le serveur MCP place le même compte derrière 51 outils au lieu de ces points de terminaison, pour un client qui parle déjà MCP plutôt que REST.
Limites
Les requêtes effectuées avec un jeton sont limitées à 1 200 par minute et
par jeton. POST /v1/billing/setup-session et POST /v1/billing/purchase
partagent un budget plus strict de 60 par minute et par compte, en amont de
l’appel Stripe que l’un ou l’autre effectue. Une requête refusée répond
429 rate_limited avec un en-tête Retry-After indiquant le délai d’attente
en secondes ; patientez ce délai avant de réessayer. Les dépôts sont
plafonnés à 5 Go et 10 heures, avec un minimum de 15 secondes, et les limites
d’admission propres au compte (max_active_jobs, max_daily_seconds) sont
rapportées par GET /v1/account.
Bac à sable
Il n’existe aucun mode d’essai : chaque appel /v1/ s’exécute contre un
compte réel, et acheter des minutes dépense de l’argent réel.
Erreurs
Une erreur est un corps JSON qui porte un code numérique stable et un slug
error, au statut HTTP indiqué ci-dessous. Les corps d’erreur /v1/ sont
une projection figée : error, code, et un ensemble fixe de clés de
détail. La forme d’erreur d’/api n’est pas figée et peut en porter
davantage. Les codes qu’un appelant automatisé doit gérer :
| Statut | Code | Slug | Signification |
|---|---|---|---|
| 401 | 2000 | unauthorized |
Jeton porteur manquant ou invalide |
| 403 | 2022 | pat_forbidden |
La route n’accepte aucun jeton |
| 403 | 2023 | pat_scope_missing |
Le jeton n’a pas le scope requis pour cet appel, ou la facturation est désactivée sur le compte |
| 403 | 4021 | pat_purchase_requires_app |
Aucune carte enregistrée n’a été nommée, ou le paiement a été refusé ; rien n’a été débité |
| 403 | 4023 | pat_purchase_requires_authentication |
L’émetteur de la carte demande une vérification que l’appelant ne peut pas effectuer ; la carte elle-même est valide |
| 402 | 3001 | insufficient_balance |
Solde insuffisant pour admettre le job |
| 402 | 3002 | card_declined |
La carte enregistrée a été refusée |
| 402 | 3003 | org_insufficient_balance |
La réserve de l’espace de travail qui finance ce dépôt est insuffisante |
| 404 | 4000 | not_found |
Aucun job, dépôt ou ressource de ce type |
| 409 | 4001 | wrong_state |
La ressource n’est pas dans un état qui permette cela |
| 422 | 1021 | duration_out_of_range |
La durée déclarée est inférieure à 15 secondes ou supérieure à 10 heures |
| 429 | 5000 | rate_limited |
Trop de requêtes ; attendez Retry-After |
| 502 | 6001 | provider_error |
Un service en amont a échoué |
| 500 | 9000 | internal_error |
Une erreur serveur inattendue |
Le document OpenAPI nomme le code exact que chaque opération peut renvoyer,
et le serveur MCP publie le catalogue complet sous forme de ressource
hushscript://error-codes.