Zum Inhalt springen
Euphona

Entwickler

Die Euphona Engine als API

Ein REST-Vertrag über api.euphonaai.com/v1. Asynchrone Jobs mit Live-Fortschritt per SSE, signierte Dateiübertragung, Schlüssel mit Präfix und ein kostenloser Testmodus: ein eigenes Produkt mit einem eigenen Supportzeitraum von zwölf Monaten.

Schlüssel werden in den Studio-Einstellungen erstellt: eu_test_… sofort und kostenlos, eu_live_… auf derselben Seite, sobald eine Karte hinterlegt ist, abgerechnet ab dem ersten Job. Der Testmodus durchläuft den vollständigen Vertrag.

Alles ist ein Job

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
  }
}

Design

Wie sie gebaut ist

Vier Entscheidungen, die nicht verhandelbar sind, weil jede von ihnen den Unterschied macht zwischen einer Integration, die ihren ersten Ausfall übersteht, und einer, die das nicht tut.

Alles ist ein Job

Endpunkte für Funktionen liefern 202 und eine Job-Ressource zurück. Es gibt keine synchronen Verarbeitungsendpunkte und wird nie welche geben: ein dreiminütiger Song in Mastering-Qualität ist kein Request-Response-Zyklus, und eine API, die etwas anderes vorgibt, bringt jeder Integration bei, falsch geschrieben zu werden.

Audio geht um die API herum, nicht durch sie hindurch

POST /v1/files liefert ein signiertes, fortsetzbares Upload-Ziel auf files.euphonaai.com. Ergebnisse kommen als signierte, ablaufende Download-URLs zurück. Die API selbst ist eine JSON-Steuerungsebene und transportiert nie ein einziges Byte Audio.

Diese API ist das Produkt, kein Nebenprodukt

Euphona Studio spricht eine andere, eigene API. Das ist Absicht: Die beiden sind getrennte Produkte, die sich eine Engine teilen, daher kann eine Änderung, die die App braucht, diesen Vertrag nicht erreichen, und der zwölfmonatige Supportzeitraum dieses Vertrags ist keine Steuer, die die App zahlen muss. Die ehrliche Konsequenz: Die App kann Dinge, die diese API nicht kann, wenn dir das wichtig ist, frag nach, dann wird daraus ein Punkt auf der Roadmap statt einer Überraschung.

Das Dokument ist der Vertrag

Das OpenAPI-3.1-Dokument ist die maßgebliche Quelle. Serverrouten und Typen werden daraus generiert, und ein Test, an dem jedes Deployment hängt, hält die ausgelieferten Routen deckungsgleich mit den deklarierten Operationen: eine Abweichung zwischen Dokumentation und Auslieferung lässt also den Build scheitern, statt etwas zu sein, worauf wir zu achten versprechen. SDKs, die aus demselben Dokument generiert werden, kommen mit der Developer Preview.

Schritt für Schritt

Wie eine Integration aussieht

Datei erstellen, die Bytes an das signierte Ziel senden, einen Job einreichen und per SSE zusehen, wie er fertig wird. Vier Schritte, und nur der dritte unterscheidet sich je nach Funktion.

1. Datei erstellen und ein Upload-Ziel holen

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. Die Bytes an files. senden, nicht an uns

# Lies upload.protocol aus, nimm keinen Wert einfach an. Der Mechanismus für
# fortsetzbare Uploads ist eine Implementierungsentscheidung innerhalb des
# Vertrags und kann sich ohne neue Hauptversion ändern: genau deshalb ist
# er ein Feld und keine Dokumentation.

3. Den Job beobachten

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. Mit dem Job selbst abgleichen

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}}

# Die Job-Ressource ist der aktuelle Stand der Zustandsmaschine: Was auch
# immer ein Stream gezeigt oder verpasst hat, lies sie erneut, bevor du
# danach handelst. Eine abgebrochene SSE-Verbindung verbindet sich mit
# Last-Event-ID neu und verliert nichts.

Referenz

Die Schnittstelle

Die Plattform-Schnittstelle, die alle Funktionen teilen. Jede Funktion fügt einen Endpunkt zum Erstellen hinzu; der Job, den er zurückgibt, durchläuft alles darunter: derselbe Lebenszyklus, dieselben Events, derselbe Bericht.

Endpunkte im v1-Vertrag
MethodePfadWas er macht
POST/v1/filesEine Datei erstellen und ein signiertes, fortsetzbares Upload-Ziel holen.
GET/v1/files/{id}Einlesestatus, gemessene Dauer, Samplerate, Kanäle, Format.
DELETE/v1/files/{id}Eine Datei löschen und alles, was daraus entstanden ist; abgelehnt, solange ein Job sie noch verwendet.
GET/v1/files/{id}/downloadEine signierte, ablaufende Download-URL.
GET/v1/jobsDie einheitliche Leseschnittstelle, mit Cursor-Paginierung.
GET/v1/jobs/{id}Status, Schritt, Fortschritt, Abrechnung und der gemessene Bericht.
POST/v1/jobs/{id}/cancelAbbrechen; keine Kosten, bevor der Verarbeitungsschritt (process) beginnt.
POST/v1/jobs/{id}/inputBeantworten, was ein Job im Status awaiting_input fragt, und ihn fortsetzen. Wird nicht berechnet.
GET/v1/jobs/{id}/eventsServer-Sent Events über die gesamte Lebensdauer des Jobs.
POST/v1/projectsDateien und Jobs gruppieren; der Track Graph gilt pro Projekt.
GET/v1/projects/{id}Ein Projekt, mit den IDs seiner Dateien und Jobs.
GET/v1/usageKontingentstand und die beiden Abrechnungstöpfe.
GET/v1/webhook-endpointsDeine Webhook-Endpunkte im Modus dieses Schlüssels.
POST/v1/webhook-endpointsRegistriere eine HTTPS-URL; sie beantwortet eine signierte Challenge, bevor irgendetwas gespeichert wird.
GET/v1/webhook-endpoints/{id}Ein Endpunkt. Sein Secret wird nie wieder angezeigt.
PATCH/v1/webhook-endpoints/{id}Ändere Ereignisse, Beschreibung oder URL; eine neue URL wird erneut verifiziert.
DELETE/v1/webhook-endpoints/{id}Löschen; nicht zugestellte Ereignisse für ihn werden verworfen.
POST/v1/webhook-endpoints/{id}/rotate-secretEin neues Signing-Secret; das alte signiert während einer Übergangszeit weiter.
GET/v1/eventsEreignisse der letzten dreißig Tage, mit dem Ergebnis jeder Zustellung.
GET/v1/events/{id}Ein Ereignis so, wie es zugestellt wurde, mit seinen Zustellungen.

Webhooks

Erfahre, wann ein Job fertig ist

Signierte Benachrichtigungen, wenn ein Job fertig ist oder auf deine Antwort wartet, so muss eine Integration weder einen Stream offen halten noch pollen.

Ereignisse
job.completed, job.failed, job.canceled und job.awaiting_input. Jedes ist ein Ereignis, dessen data die Job-Ressource genau so ist, wie Polling sie liefert.
Workspace-Ereignisse
file.expiring, wenn für einen gespeicherten Upload ein Datum feststeht, ab dem er entfernt werden kann, mit der Datei als data, und usage.threshold, wenn ein Nutzungskontingent 80 % oder 100 % seines Limits erreicht. Beide erreichen Endpunkte in jedem Modus, einmal pro Datei bzw. einmal pro Schwelle und Zeitraum.
Registrierung
Erfordert einen Schlüssel mit dem Scope webhooks:manage, den neue Schlüssel nicht haben. Die URL muss HTTPS verwenden und darf nur in öffentliche Adressen aufgelöst werden, und sie muss eine signierte Challenge mit einer 2xx-Antwort zurückgeben, bevor irgendetwas gespeichert wird. Zustellungen werden nur über IPv4 gesendet, daher muss der Host eine IPv4-Adresse haben.
Zustellung
Mindestens einmal, dedupliziere also anhand von Euphona-Event-Id. Alles außer einer 2xx-Antwort innerhalb von 10 Sekunden wird 24 Stunden lang mit exponentiellem Backoff wiederholt, danach als unzustellbar markiert und in GET /v1/events aufbewahrt. Weiterleitungen werden nie verfolgt, und die Reihenfolge ist nicht garantiert: Gleiche mit dem Job ab.
Signaturen
Euphona-Signature enthält einen Zeitstempel und einen HMAC-SHA256 über Zeitstempel, Punkt und den unveränderten Body, mit dem Secret des Endpunkts als Schlüssel. Prüfe ihn vor dem Parsen, vergleiche in konstanter Zeit und lehne Zeitstempel ab, die mehr als fünf Minuten von deiner Uhr abweichen.
Absenderadressen
Zustellungen werden von 65.21.69.162 gesendet. Lass diese Adressen zu, wenn deine Firewall eingehenden Datenverkehr filtert, und prüfe die Signatur trotzdem: Die Adresse, von der eine Anfrage kommt, beweist nicht, wer sie gesendet hat.

Eine Zustellung prüfen (Node.js)

import { createHmac, timingSafeEqual } from "node:crypto";

// Übergib den Body genau so, wie er angekommen ist: Geparstes und neu
// serialisiertes JSON ist nicht die Nachricht, die signiert wurde.
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);
  });
}

Betrieb

Was dich um drei Uhr morgens erwischt

Die sechs Verhaltensweisen, die du lesen solltest, bevor du die Integration schreibst: nicht erst, nachdem sie dich geweckt hat.

Schlüssel
eu_live_… und eu_test_…, im Besitz eines Workspaces, einmal bei der Erstellung angezeigt und gehasht gespeichert. Rotiere mit einem Überlappungsfenster von bis zu dreißig Tagen, damit eine Rotation kein Ausfall ist. Testschlüssel sind kostenlos bis zu ihrem eigenen monatlichen Limit und laufen in ihrer eigenen Scheduling-Klasse; sie liefern eine Vorschau statt eines Masters, sodass sich eine Integration von Anfang bis Ende bauen lässt, ohne etwas zu kaufen.
Fehler
Immer RFC 9457 application/problem+json, mit einem stabilen, maschinenlesbaren code, einem menschenlesbaren detail und der Request-ID. Verzweige nach code, nie nach dem Text: der Text ist lokalisiert, der Code ist Vertrag.
Idempotenz
Idempotency-Key bei jedem POST. Derselbe Schlüssel mit demselben Body liefert die ursprüngliche Antwort erneut, solange die Ressource existiert, die er erstellt hat; derselbe Schlüssel mit einem anderen Body wird abgelehnt. Ein erneuter Versuch nach einem Timeout gibt also den ursprünglichen Job zurück, statt einen zweiten zu starten, der dir berechnet würde.
Limits
Gleitendes Fenster pro Schlüssel mit den Standard-Headern RateLimit-*; 60 Anfragen pro Minute bei Abrechnung nach Verbrauch, mehr mit einem Vertrag über eine Mindestabnahme. Der Limiter regelt nur die Anfragehäufigkeit: den Durchsatz regeln dein Kontingent und die Warteschlange.
Zwei Zähler, nie einer
Jobs mit API-Schlüssel werden über den Topf für Abrechnung nach Verbrauch abgerechnet; Jobs aus einer Studio-Sitzung verbrauchen Planminuten. Kein Topf kann den anderen leeren, und GET /v1/usage meldet beide. Ausgabenlimits sind standardmäßig aktiv. Speicherplatz ist ein Kontingent, nicht zwei: Mit einem Schlüssel hochgeladene Dateien und die Ergebnisse von API-Jobs werden zusammen mit allem aus dem Studio auf das Speicherkontingent des Workspace-Plans angerechnet, 2 GB im kostenlosen Plan, und ein Upload oder Job, der nicht mehr hineinpasst, wird mit storage_full abgelehnt.
Request-Id
Bei jeder Antwort, ob Erfolg oder Fehler. Nenn sie in einer Support-Anfrage, und sie lässt sich von Anfang bis Ende zurückverfolgen: durch den Job, durch jeden Schritt, bis zu dem, was tatsächlich passiert ist.
Fehler
codetitle
unauthenticatedAuthentifizierung erforderlich
invalid_keyUngültiger API-Schlüssel
key_revokedAPI-Schlüssel widerrufen
insufficient_scopeUnzureichender Scope
email_verification_requiredE-Mail-Bestätigung erforderlich
malformed_requestFehlerhafte Anfrage
validation_failedValidierung der Anfrage fehlgeschlagen
unsupported_media_typeNicht unterstützter Medientyp
idempotency_key_reuseIdempotenzschlüssel mit anderem Body wiederverwendet
not_foundNicht gefunden
job_not_cancelableJob kann nicht abgebrochen werden
job_not_awaiting_inputJob wartet nicht auf eine Eingabe
file_not_readyDatei ist nicht bereit
file_expiredDatei ist abgelaufen
file_in_useDatei wird verwendet
file_disabledDatei aus rechtlichen Gründen nicht verfügbar
unsupported_formatNicht unterstütztes Audioformat
unsupported_sample_rateNicht unterstützte Samplerate
unsupported_channel_countNicht unterstützte Kanalzahl
file_too_longAudio ist zu lang
file_too_shortAudio ist zu kurz
file_too_largeDatei ist zu groß
corrupt_mediaAudio konnte nicht dekodiert werden
rate_limitedRate-Limit überschritten
too_many_active_jobsZu viele aktive Jobs
quota_exceededKontingent überschritten
spend_cap_reachedAusgabenlimit erreicht
test_mode_cap_reachedTestmodus-Limit erreicht
storage_fullSpeicherplatz voll
purchase_requiredKauf erforderlich
payment_method_requiredZahlungsmethode erforderlich
format_not_in_planFormat in diesem Plan nicht enthalten
webhook_url_not_httpsWebhook-URL muss HTTPS verwenden
webhook_url_not_publicWebhook-URL muss öffentlich erreichbar sein
webhook_verification_failedÜberprüfung der Webhook-Inhaberschaft fehlgeschlagen
internal_errorInterner Fehler
service_unavailableDienst nicht verfügbar
processing_failedVerarbeitung fehlgeschlagen
capacity_unavailableKapazität nicht verfügbar
capability_pausedVorübergehend pausiert
workspace_suspendedWorkspace gesperrt
uploads_blockedUploads pausiert
reauthentication_requiredBestätige, dass du es bist

Tarife

Was es kostet

Pro verarbeiteter Minute, mal dem Faktor der Funktion, monatlich abgerechnet. Kein Abo nötig, ein API-Workspace kann im kostenlosen Plan sein. Der Testmodus ist kostenlos.

Die ersten 100 Minuten im Monat
0,35 $
Bis 1.000
0,25 $
Bis 10.000
0,15 $
Darüber hinaus oder mit Mindestabnahme
Sprich mit uns