Développeurs
Le moteur Euphona, en API
Un seul contrat REST sur api.euphonaai.com/v1. Des tâches asynchrones avec progression SSE en direct, un transfert de fichiers signé, des clés préfixées et un mode test gratuit : un produit à part entière, avec sa propre fenêtre de support de douze mois.
Les clés se créent dans les paramètres du Studio : eu_test_… immédiatement et gratuitement, eu_live_… depuis le même écran une fois une carte enregistrée, avec un décompte dès sa première tâche. Le mode test exécute l’intégralité du contrat.
Tout est une tâche
curl -X POST https://api.euphonaai.com/v1/masters \
-H "Authorization: Bearer $EUPHONA_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"file_id": "file_8Kd2mQTz3XaV7pLw"}'
HTTP/2 202
{
"id": "job_7Vd2mHs5Yq8Tz3Xa",
"object": "job",
"type": "mastering",
"status": "queued",
"stage": null,
"metering": {
"pool": "payg",
"charged_minutes": 0,
"test_mode": false
}
}Conception
Comment elle est conçue
Quatre décisions non négociables, parce que chacune fait la différence entre une intégration qui survit à sa première panne et une intégration qui n’y survit pas.
Tout est une tâche
L’audio contourne l’API, il ne la traverse pas
Cette API est le produit, pas un sous-produit
Le document est le contrat
Pas à pas
La forme d’une intégration
Créez un fichier, envoyez les octets vers la destination signée, soumettez une tâche et suivez-la jusqu’au bout via SSE. Quatre étapes, et la troisième est la seule qui change d’un traitement à l’autre.
1. Créer le fichier et obtenir une destination d’import
curl -X POST https://api.euphonaai.com/v1/files \
-H "Authorization: Bearer $EUPHONA_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"filename":"take-07.wav","bytes":58210044,"content_type":"audio/wav"}'
{
"id": "file_8Kd2mQ...",
"object": "file",
"status": "awaiting_upload",
"upload": { "protocol": "...", "url": "https://files.euphonaai.com/...", "expires_at": "..." }
}2. Envoyer les octets à files., pas à nous
# Lisez upload.protocol, ne supposez rien. Le mécanisme de reprise est un choix
# d’implémentation interne au contrat, qui peut changer sans nouvelle version
# majeure : c’est précisément pour cela que c’est un champ, pas de la doc.3. Suivre la tâche
curl -N https://api.euphonaai.com/v1/jobs/job_4Rb.../events \
-H "Authorization: Bearer $EUPHONA_API_KEY"
event: job.progress
data: {"status":"analyzing","stage":"analyze","progress":{"fraction":0.4}}
event: job.completed
data: {"status":"succeeded","result":{...},"metering":{"pool":"payg","charged_minutes":3.4}}4. Revérifier auprès de la tâche elle-même
curl https://api.euphonaai.com/v1/jobs/job_4Rb... \
-H "Authorization: Bearer $EUPHONA_API_KEY"
{"id":"job_4Rb...","status":"succeeded","result":{...},
"metering":{"pool":"payg","charged_minutes":3.4}}
# La ressource de tâche est la vérité actuelle de la machine à états : quoi
# qu’un flux ait montré ou manqué, relisez-la avant d’agir. Une connexion SSE
# interrompue se reconnecte avec Last-Event-ID et ne perd rien.Référence
La surface
La surface de la plateforme, commune à tous les traitements. Chaque traitement ajoute un point de terminaison de création ; la tâche qu’il renvoie passe par tout ce qui suit : le même cycle de vie, les mêmes événements, le même rapport.
| Méthode | Chemin | Rôle |
|---|---|---|
| POST | /v1/files | Créer un fichier et obtenir une destination d’import signée, avec reprise. |
| GET | /v1/files/{id} | État de l’ingestion, durée mesurée, fréquence d’échantillonnage, canaux, format. |
| DELETE | /v1/files/{id} | Supprimer un fichier et ce qui en a été produit ; refusé tant qu’une tâche l’utilise encore. |
| GET | /v1/files/{id}/download | Une URL de téléchargement signée qui expire. |
| GET | /v1/jobs | La surface de lecture unifiée, paginée par curseur. |
| GET | /v1/jobs/{id} | État, étape, progression, décompte et rapport mesuré. |
| POST | /v1/jobs/{id}/cancel | Annuler ; rien n’est facturé avant le début de l’étape de traitement. |
| POST | /v1/jobs/{id}/input | Répondre à ce que demande une tâche à l’état awaiting_input, et la reprendre. Non décompté. |
| GET | /v1/jobs/{id}/events | Des événements envoyés par le serveur (SSE) pendant toute la vie de la tâche. |
| POST | /v1/projects | Regrouper des fichiers et des tâches ; le Track Graph est propre à chaque projet. |
| GET | /v1/projects/{id} | Un projet, avec les identifiants de ses fichiers et de ses tâches. |
| GET | /v1/usage | L’état du quota et les deux réserves de décompte. |
| GET | /v1/webhook-endpoints | Vos points de terminaison de webhook, dans le mode de cette clé. |
| POST | /v1/webhook-endpoints | Enregistrez une URL https ; elle répond à un challenge signé avant que quoi que ce soit ne soit enregistré. |
| GET | /v1/webhook-endpoints/{id} | Un point de terminaison. Son secret n’est plus jamais affiché. |
| PATCH | /v1/webhook-endpoints/{id} | Modifiez ses événements, sa description ou son URL ; une nouvelle URL est vérifiée à nouveau. |
| DELETE | /v1/webhook-endpoints/{id} | Le supprimer ; les événements non livrés qui lui étaient destinés sont abandonnés. |
| POST | /v1/webhook-endpoints/{id}/rotate-secret | Un nouveau secret de signature ; l’ancien continue de signer pendant une période de chevauchement. |
| GET | /v1/events | Trente jours d’événements, avec le résultat de chaque livraison. |
| GET | /v1/events/{id} | Un événement tel qu’il a été livré, avec ses livraisons. |
Webhooks
Savoir quand une tâche est terminée
Des notifications signées quand une tâche se termine ou attend votre réponse, pour qu’une intégration n’ait ni à garder un flux ouvert ni à interroger l’API en boucle.
- Événements
job.completed,job.failed,job.canceledetjob.awaiting_input. Chacun est un événement dontdataest la ressource de la tâche exactement telle que l’interrogation la renvoie.- Événements de l’espace de travail
file.expiringlorsqu’un fichier importé et stocké reçoit une date à partir de laquelle il peut être supprimé, avec le fichier commedata, etusage.thresholdlorsqu’un volume d’utilisation atteint 80 % ou 100 % de sa limite. Les deux parviennent aux points de terminaison des deux modes, une fois par fichier ou une fois par seuil et par période.- Enregistrement
- Nécessite une clé dotée de la portée
webhooks:manage, que les nouvelles clés n’ont pas. L’URL doit utiliser https et ne se résoudre qu’en adresses publiques, et elle doit répondre à un challenge signé par une réponse 2xx qui le renvoie avant que quoi que ce soit ne soit enregistré. Les livraisons sont envoyées uniquement en IPv4 : l’hôte doit donc avoir une adresse IPv4. - Livraison
- Au moins une fois : dédupliquez donc sur
Euphona-Event-Id. Tout ce qui n’est pas une réponse 2xx en 10 secondes est retenté avec un backoff exponentiel pendant 24 heures, puis placé en lettre morte et conservé dansGET /v1/events. Les redirections ne sont jamais suivies et l’ordre n’est pas garanti : rapprochez-vous de la tâche. - Signatures
Euphona-Signaturecontient un horodatage et un HMAC-SHA256 de l’horodatage, d’un point et du corps brut, avec le secret du point de terminaison pour clé. Vérifiez-le avant l’analyse, comparez en temps constant et refusez un horodatage éloigné de plus de cinq minutes de votre horloge.- Adresses d’origine
- Les livraisons sont envoyées depuis
65.21.69.162. Autorisez-les si votre pare-feu filtre le trafic entrant, et vérifiez quand même la signature : l’adresse d’où provient une requête ne prouve pas qui l’a envoyée.
Vérifier une livraison (Node.js)
import { createHmac, timingSafeEqual } from "node:crypto";
// Passez le corps exactement tel qu’il est arrivé : du JSON analysé puis
// resérialisé n’est pas le message qui a été signé.
export function verifyEuphonaSignature(rawBody, header, secret, now = Date.now() / 1000) {
const parts = header.split(",").map((part) => part.trim().split("="));
const t = Number(parts.find(([key]) => key === "t")?.[1]);
const signatures = parts.filter(([key]) => key === "v1").map(([, value]) => value);
if (!Number.isFinite(t) || Math.abs(now - t) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
return signatures.some((value) => {
const given = Buffer.from(value, "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
});
}Exploitation
Les pièges de trois heures du matin
Les six comportements à lire avant d’écrire l’intégration, plutôt qu’après qu’elle vous a réveillé en pleine nuit.
- Clés
eu_live_…eteu_test_…, rattachées à un espace de travail, affichées une seule fois à la création et stockées sous forme hachée. Faites-les tourner avec une fenêtre de chevauchement allant jusqu’à trente jours : une rotation n’est donc pas une panne. Les clés de test sont gratuites dans la limite de leur propre plafond mensuel et s’exécutent dans leur propre classe de planification ; elles renvoient une pré-écoute plutôt qu’un master, si bien qu’une intégration peut être construite de bout en bout sans rien acheter.- Erreurs
- RFC 9457
application/problem+json, toujours, avec uncodestable et lisible par machine, undetaillisible par un humain et l’identifiant de requête. Basez votre logique surcode, jamais sur le texte : le texte est traduit, le code est un contrat. - Idempotence
Idempotency-Keysur chaque POST. La même clé avec le même corps rejoue la réponse d’origine tant que la ressource créée existe ; la même clé avec un corps différent est refusée. Une nouvelle tentative après un dépassement de délai renvoie donc la tâche d’origine au lieu d’en lancer une seconde qui vous serait facturée.- Limites
- Fenêtre glissante par clé, avec les en-têtes standard
RateLimit-*; 60 requêtes par minute en paiement à l’usage, davantage avec un contrat d’engagement de volume. Le limiteur ne régit que la fréquence des requêtes : le débit dépend de votre quota et de la file d’attente. - Deux compteurs, jamais un seul
- Les tâches lancées avec une clé API sont décomptées sur la réserve à l’usage ; les tâches lancées depuis une session Studio consomment les minutes de la formule. Aucune réserve ne peut vider l’autre, et
GET /v1/usageindique les deux. Les plafonds de dépenses sont activés par défaut. Le stockage est un seul quota, pas deux : les fichiers importés avec une clé et les résultats des tâches de l’API sont décomptés, avec tout ce qui vient du Studio, du quota de stockage de la formule de l’espace de travail, 2 Go en formule gratuite, et un import ou une tâche qui n’y tiendrait pas est refusé avecstorage_full. - Request-Id
- Sur chaque réponse, succès comme échec. Indiquez-le dans une demande d’assistance et elle se résout de bout en bout : à travers la tâche, à travers chaque étape, jusqu’à ce qui s’est réellement passé.
code | title |
|---|---|
| unauthenticated | Authentification requise |
| invalid_ | Clé API non valide |
| key_ | Clé API révoquée |
| insufficient_ | Portée insuffisante |
| email_ | Vérification de l’adresse e-mail requise |
| malformed_ | Requête mal formée |
| validation_ | Échec de la validation de la requête |
| unsupported_ | Type de média non pris en charge |
| idempotency_ | Clé d’idempotence réutilisée avec un autre corps |
| not_ | Introuvable |
| job_ | La tâche ne peut pas être annulée |
| job_ | La tâche n’attend pas de réponse |
| file_ | Le fichier n’est pas prêt |
| file_ | Le fichier a expiré |
| file_ | Fichier en cours d’utilisation |
| file_ | Fichier indisponible pour des raisons juridiques |
| unsupported_ | Format audio non pris en charge |
| unsupported_ | Fréquence d’échantillonnage non prise en charge |
| unsupported_ | Nombre de canaux non pris en charge |
| file_ | Audio trop long |
| file_ | Audio trop court |
| file_ | Fichier trop volumineux |
| corrupt_ | L’audio n’a pas pu être décodé |
| rate_ | Limite de requêtes dépassée |
| too_ | Trop de tâches en cours |
| quota_ | Quota dépassé |
| spend_ | Plafond de dépenses atteint |
| test_ | Plafond du mode test atteint |
| storage_ | Stockage plein |
| purchase_ | Achat requis |
| payment_ | Moyen de paiement requis |
| format_ | Format non inclus dans cette formule |
| webhook_ | L’URL du webhook doit être en HTTPS |
| webhook_ | L’URL du webhook doit être accessible publiquement |
| webhook_ | Échec de la vérification de propriété du webhook |
| internal_ | Erreur interne |
| service_ | Service indisponible |
| processing_ | Échec du traitement |
| capacity_ | Capacité indisponible |
| capability_ | Temporairement suspendu |
| workspace_ | Espace de travail suspendu |
| uploads_ | Imports suspendus |
| reauthentication_ | Confirmez qu’il s’agit bien de vous |
Tarifs
Ce que cela coûte
À la minute traitée, multipliée par le coefficient du traitement, facturée chaque mois. Aucun abonnement requis, un espace de travail API peut être en formule gratuite. Le mode test est gratuit.
- 100 premières minutes du mois
- 0,35 $US
- Jusqu’à 1 000
- 0,25 $US
- Jusqu’à 10 000
- 0,15 $US
- Au-delà, ou avec engagement de volume
- Nous contacter