Przejdź do treści
Euphona

Dla programistów

Silnik Euphona jako API

Jeden kontrakt REST pod adresem api.euphonaai.com/v1. Asynchroniczne zadania z postępem na żywo przez SSE, podpisany transfer plików, klucze z prefiksem i bezpłatny tryb testowy: pełnoprawny produkt z własnym dwunastomiesięcznym okresem wsparcia.

Klucze tworzy się w ustawieniach Studia: eu_test_… od razu i bezpłatnie, eu_live_… na tym samym ekranie, gdy zapiszesz kartę, naliczany od pierwszego zadania. Tryb testowy obsługuje cały kontrakt.

Wszystko jest zadaniem

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

Projekt

Jak to zaprojektowano

Cztery decyzje, które nie podlegają negocjacji, bo każda z nich oddziela integrację, która przetrwa pierwszą awarię, od takiej, która jej nie przetrwa.

Wszystko jest zadaniem

Endpointy funkcji zwracają 202 i zasób zadania. Nie ma synchronicznych endpointów przetwarzania i nigdy nie będzie: trzyminutowa piosenka w jakości masteringu to nie cykl żądanie-odpowiedź, a API, które udaje, że jest inaczej, uczy pisać każdą integrację źle.

Dźwięk omija API, a nie przez nie przechodzi

POST /v1/files zwraca podpisany, wznawialny cel przesyłania na hoście files.euphonaai.com. Wyniki wracają jako podpisane, wygasające URL-e do pobrania. Samo API to płaszczyzna sterowania w JSON i nigdy nie przenosi ani bajtu dźwięku.

To API jest produktem, a nie produktem ubocznym

Euphona Studio korzysta z innego, własnego API. To celowe: to dwa osobne produkty korzystające z jednego silnika, więc zmiana, której potrzebuje aplikacja, nie może dotknąć tego kontraktu, a dwunastomiesięczny okres wsparcia tego kontraktu nie jest podatkiem, który musi płacić aplikacja. Uczciwa konsekwencja jest taka, że aplikacja może robić rzeczy, których to API nie robi: jeśli to dla ciebie ważne, zapytaj, a stanie się to pozycją na mapie drogowej, a nie niespodzianką.

Dokument jest kontraktem

Dokument OpenAPI 3.1 jest źródłem prawdy. Trasy serwera i typy są z niego generowane, a test blokujący wdrożenie pilnuje, by udostępniane trasy były równe zadeklarowanym operacjom: więc rozbieżność między tym, co udokumentowane, a tym, co udostępniane, przerywa kompilację, zamiast być czymś, czego obiecujemy pilnować. SDK wygenerowane z tego samego dokumentu pojawią się razem z wersją zapoznawczą dla programistów.

Przewodnik

Kształt integracji

Utwórz plik, wyślij dane do podpisanego celu, zleć zadanie i obserwuj jego zakończenie przez SSE. Cztery kroki, a tylko trzeci różni się w zależności od funkcji.

1. Utwórz plik i uzyskaj cel przesyłania

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. Wyślij dane na files., nie do nas

# Odczytaj upload.protocol, nie zakładaj go z góry. Mechanizm wznawiania to wybór
# implementacyjny wewnątrz kontraktu i może się zmienić bez podbicia wersji
# głównej: właśnie dlatego jest polem, a nie dokumentacją.

3. Obserwuj zadanie

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. Uzgodnij stan z samym zadaniem

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

# Zasób zadania to bieżąca prawda maszyny stanów: cokolwiek strumień
# pokazał lub pominął, odczytaj go ponownie, zanim zaczniesz działać. Zerwane
# połączenie SSE wznawia się z Last-Event-ID i niczego nie traci.

Dokumentacja

Powierzchnia API

Wspólna powierzchnia platformy dla wszystkich funkcji. Każda funkcja dodaje jeden endpoint tworzący; zadanie, które zwraca, przechodzi przez wszystko poniżej: ten sam cykl życia, te same zdarzenia, ten sam raport.

Endpointy w kontrakcie v1
MetodaŚcieżkaCo robi
POST/v1/filesTworzy plik i zwraca podpisany, wznawialny cel przesyłania.
GET/v1/files/{id}Stan przyjęcia, zmierzony czas trwania, częstotliwość próbkowania, kanały, format.
DELETE/v1/files/{id}Usuwa plik i wszystko, co z niego powstało; odrzucane, dopóki zadanie nadal go używa.
GET/v1/files/{id}/downloadPodpisany, wygasający URL do pobrania.
GET/v1/jobsUjednolicony odczyt, ze stronicowaniem kursorem.
GET/v1/jobs/{id}Status, etap, postęp, naliczanie i zmierzony raport.
POST/v1/jobs/{id}/cancelAnuluje; bez opłaty przed rozpoczęciem etapu przetwarzania.
POST/v1/jobs/{id}/inputOdpowiada na pytanie zadania w stanie awaiting_input i wznawia je. Nienaliczane.
GET/v1/jobs/{id}/eventsZdarzenia wysyłane przez serwer przez cały czas życia zadania.
POST/v1/projectsGrupuje pliki i zadania; Track Graph istnieje osobno dla każdego projektu.
GET/v1/projects/{id}Jeden projekt z identyfikatorami jego plików i zadań.
GET/v1/usageStan limitu i dwie pule naliczania.
GET/v1/webhook-endpointsTwoje endpointy webhooków w trybie tego klucza.
POST/v1/webhook-endpointsZarejestruj URL https; zanim cokolwiek zostanie zapisane, odpowiada on na podpisane wyzwanie.
GET/v1/webhook-endpoints/{id}Jeden endpoint. Jego sekret nie jest już nigdy pokazywany.
PATCH/v1/webhook-endpoints/{id}Zmień zdarzenia, opis lub URL; nowy URL jest weryfikowany ponownie.
DELETE/v1/webhook-endpoints/{id}Usuń go; niedostarczone do niego zdarzenia przepadają.
POST/v1/webhook-endpoints/{id}/rotate-secretNowy sekret podpisu; stary podpisuje dalej przez okres przejściowy.
GET/v1/eventsZdarzenia z trzydziestu dni z wynikiem każdej dostawy.
GET/v1/events/{id}Jedno zdarzenie w postaci, w jakiej zostało dostarczone, z jego dostawami.

Webhooki

Dowiedz się, kiedy zadanie jest gotowe

Podpisane powiadomienia, gdy zadanie się kończy albo czeka na twoją odpowiedź: integracja nie musi trzymać otwartego strumienia ani odpytywać API.

Zdarzenia
job.completed, job.failed, job.canceled i job.awaiting_input. Każde to zdarzenie, którego data jest zasobem zadania dokładnie takim, jaki zwraca odpytywanie.
Zdarzenia obszaru roboczego
file.expiring, gdy przechowywany przesłany plik otrzymuje datę, od której może zostać usunięty, z plikiem jako data, oraz usage.threshold, gdy pula wykorzystania osiąga 80% lub 100% swojego limitu. Oba trafiają do endpointów w obu trybach, raz na plik albo raz na próg i okres.
Rejestracja
Wymaga klucza z zakresem webhooks:manage, którego nowe klucze nie mają. URL musi używać https i rozwiązywać się wyłącznie na adresy publiczne, a zanim cokolwiek zostanie zapisane, musi odpowiedzieć na podpisane wyzwanie odpowiedzią 2xx, która je odsyła. Dostawy są wysyłane wyłącznie przez IPv4, więc host musi mieć adres IPv4.
Dostawa
Co najmniej raz, więc usuwaj duplikaty według Euphona-Event-Id. Wszystko poza odpowiedzią 2xx w ciągu 10 sekund jest ponawiane z wykładniczym odstępem przez 24 godziny, potem trafia do kolejki martwych wiadomości i zostaje w GET /v1/events. Przekierowania nigdy nie są wykonywane, a kolejność nie jest gwarantowana: uzgadniaj stan z zadaniem.
Podpisy
Euphona-Signature zawiera znacznik czasu i HMAC-SHA256 znacznika czasu, kropki i surowej treści, z sekretem endpointu jako kluczem. Sprawdź go przed parsowaniem, porównuj w stałym czasie i odrzucaj znacznik czasu odbiegający od twojego zegara o więcej niż pięć minut.
Adresy źródłowe
Dostawy są wysyłane z 65.21.69.162. Zezwól na nie, jeśli twoja zapora filtruje ruch przychodzący, i mimo to weryfikuj podpis: adres, z którego przychodzi żądanie, nie dowodzi, kto je wysłał.

Weryfikacja dostawy (Node.js)

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

// Przekaż treść dokładnie w takiej postaci, w jakiej przyszła: JSON sparsowany
// i zserializowany ponownie nie jest wiadomością, którą podpisano.
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);
  });
}

Eksploatacja

Rzeczy, które gryzą o trzeciej w nocy

Sześć zachowań, o których warto przeczytać, zanim napiszesz integrację, a nie wtedy, gdy cię obudzi.

Klucze
eu_live_… i eu_test_…, należące do przestrzeni roboczej, pokazywane raz przy tworzeniu i przechowywane jako skrót. Rotacja z okresem nakładania się do trzydziestu dni, więc rotacja nie oznacza przerwy w działaniu. Klucze testowe są bezpłatne w ramach własnego miesięcznego limitu i działają we własnej klasie harmonogramowania; zwracają podgląd zamiast masteru, więc integrację można zbudować od początku do końca, niczego nie kupując.
Błędy
Zawsze RFC 9457 application/problem+json, ze stałym, czytelnym maszynowo code, czytelnym dla ludzi detail i identyfikatorem żądania. Rozgałęziaj logikę według code, nigdy według treści: treść jest tłumaczona, a kod jest kontraktem.
Idempotencja
Idempotency-Key przy każdym POST. Ten sam klucz z tą samą treścią odtwarza pierwotną odpowiedź, dopóki istnieje utworzony zasób; ten sam klucz z inną treścią jest odrzucany. Ponowienie po przekroczeniu czasu zwraca więc pierwotne zadanie, a nie uruchamia drugiego, za które zapłacisz.
Limity
Przesuwne okno dla każdego klucza ze standardowymi nagłówkami RateLimit-*; 60 żądań na minutę przy płatności za zużycie, więcej w ramach umowy z zobowiązaniem wolumenowym. Limiter ogranicza tylko częstotliwość żądań: przepustowość zależy od twojego limitu minut i kolejki.
Dwa liczniki, nigdy jeden
Zadania z kluczem API są naliczane z puli płatności za zużycie; zadania z sesji Studia zużywają minuty z planu. Żadna pula nie może wyczerpać drugiej, a GET /v1/usage raportuje obie. Limity wydatków są domyślnie włączone. Miejsce na dane to jeden limit, nie dwa: pliki przesłane z kluczem i wyniki zadań API wliczają się, razem ze wszystkim ze Studia, do limitu miejsca na dane w planie przestrzeni roboczej, 2 GB w bezpłatnym planie, a przesyłanie lub zadanie, które się nie zmieści, jest odrzucane z kodem storage_full.
Request-Id
Przy każdej odpowiedzi, udanej lub nie. Podaj go w zgłoszeniu do wsparcia, a pozwoli prześledzić wszystko od początku do końca: przez zadanie, przez każdy etap, aż do tego, co faktycznie się stało.
Błędy
codetitle
unauthenticatedWymagane uwierzytelnienie
invalid_keyNieprawidłowy klucz API
key_revokedKlucz API unieważniony
insufficient_scopeNiewystarczający zakres
email_verification_requiredWymagane potwierdzenie adresu e-mail
malformed_requestNieprawidłowo sformułowane żądanie
validation_failedWalidacja żądania nie powiodła się
unsupported_media_typeNieobsługiwany typ nośnika
idempotency_key_reuseKlucz idempotencji użyty ponownie z inną treścią
not_foundNie znaleziono
job_not_cancelableZadania nie można anulować
job_not_awaiting_inputZadanie nie czeka na odpowiedź
file_not_readyPlik nie jest gotowy
file_expiredPlik wygasł
file_in_usePlik jest w użyciu
file_disabledPlik niedostępny z przyczyn prawnych
unsupported_formatNieobsługiwany format audio
unsupported_sample_rateNieobsługiwana częstotliwość próbkowania
unsupported_channel_countNieobsługiwana liczba kanałów
file_too_longNagranie jest za długie
file_too_shortNagranie jest za krótkie
file_too_largePlik jest za duży
corrupt_mediaNie udało się zdekodować nagrania
rate_limitedPrzekroczono limit żądań
too_many_active_jobsZa dużo aktywnych zadań
quota_exceededPrzekroczono limit minut
spend_cap_reachedOsiągnięto limit wydatków
test_mode_cap_reachedOsiągnięto limit trybu testowego
storage_fullBrak miejsca na dane
purchase_requiredWymagany zakup
payment_method_requiredWymagana metoda płatności
format_not_in_planFormat niedostępny w tym planie
webhook_url_not_httpsURL webhooka musi używać HTTPS
webhook_url_not_publicURL webhooka musi być dostępny publicznie
webhook_verification_failedWeryfikacja własności webhooka nie powiodła się
internal_errorBłąd wewnętrzny
service_unavailableUsługa niedostępna
processing_failedPrzetwarzanie nie powiodło się
capacity_unavailableBrak mocy przetwarzania
capability_pausedTymczasowo wstrzymane
workspace_suspendedPrzestrzeń robocza zawieszona
uploads_blockedPrzesyłanie wstrzymane
reauthentication_requiredPotwierdź, że to ty

Stawki

Ile to kosztuje

Za każdą przetworzoną minutę, razy mnożnik funkcji, rozliczane co miesiąc. Subskrypcja nie jest wymagana: przestrzeń robocza API może być w bezpłatnym planie. Tryb testowy jest bezpłatny.

Pierwsze 100 min w miesiącu
0,35 USD
Do 1000 min
0,25 USD
Do 10 000 min
0,15 USD
Powyżej tego progu albo zobowiązanie wolumenowe
Porozmawiaj z nami