
Integracja z API AfterTalk: od nagrania do oceny
Co dostajesz z API
Integracja sprowadza się do trzech rzeczy: wysyłasz nagranie, czekasz na wynik, odbierasz ocenę. Wszystko poniżej działa na kluczu API, bez logowania użytkownika.

Zanim zaczniesz
- Wygeneruj klucz w Ustawienia → API
- Zapisz go w menedżerze sekretów — pokazujemy go tylko raz
- Sprawdź, czy projekt ma zdefiniowaną kartę oceny
- klucz API wygenerowany
- karta oceny gotowa
- webhook podpięty
- pierwsze nagranie wysłane
Wysyłanie nagrania
Nagranie wysyłasz jako multipart/form-data na endpoint projektu:
curl -X POST https://app.aftertalk.co/api/v1/projects/$PROJECT_ID/calls/upload \
-H "X-API-Key: $AFTERTALK_API_KEY" \
-F "file=@rozmowa-2026-07-14.mp3" \
-F "channel_roles=agent,customer"
W odpowiedzi dostajesz identyfikator zadania:
{
"jobId": "8f7e6d5c-4b3a-2910-8f7e-6d5c4b3a2910",
"status": "PENDING",
"createdAt": "2026-07-14T09:12:04Z"
}
Nagranie wysłane rano jest gotowe przed końcem dnia. To różnica między reagowaniem na problem a opisywaniem go w podsumowaniu kwartału.
Obsługa w kodzie
Klient w Kotlinie, z ponowieniem przy 429:
suspend fun upload(file: File, projectId: UUID): AsyncJob =
retry(times = 3, on = { it.status == 429 }) {
client.submitFormWithBinaryData(
url = "/v1/projects/$projectId/calls/upload",
formData = formData {
append("file", file.readBytes(), Headers.build {
append(HttpHeaders.ContentDisposition, "filename=\"${file.name}\"")
})
},
).body()
}
To samo w TypeScripcie:
const form = new FormData();
form.append('file', file);
const res = await fetch(`${BASE}/v1/projects/${projectId}/calls/upload`, {
method: 'POST',
headers: { 'X-API-Key': process.env.AFTERTALK_API_KEY! },
body: form,
});
if (!res.ok) throw new Error(`Upload failed: ${res.status}`);
Statusy zadania
| Status | Co oznacza | Co robić |
|---|---|---|
PENDING | Nagranie czeka w kolejce | Czekać |
PROCESSING | Trwa transkrypcja i ocena według karty | Czekać |
COMPLETED | Wynik gotowy do pobrania | Pobrać transcriptId |
FAILED | Nie udało się przetworzyć nagrania | Sprawdzić format i długość pliku |
PAYMENT_REQUIRED | Brak środków na koncie | Doładować i wysłać ponownie |

Odbieranie wyniku
Zamiast odpytywać o status, podepnij webhook. Zarejestruj adres w Ustawienia → Webhooki, a my wyślemy POST na Twój endpoint, gdy rozmowa zostanie przetworzona.
{
"event": "call.processed",
"transcriptId": "3a2910-8f7e-6d5c",
"score": 0.82,
"criteria": [
{ "name": "Potwierdzenie danych klienta", "passed": true },
{ "name": "Propozycja rozwiązania", "passed": false }
]
}
Każde żądanie jest podpisane nagłówkiem X-AfterTalk-Signature. Weryfikacja:
import hashlib
import hmac
def verify(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
Nagłówki, które wysyłamy
Zwróć uwagę na X-AfterTalk-Delivery — ten sam identyfikator wraca przy ponowieniu, więc po nim rozpoznasz duplikat.
Czego nie robić
- nie odpytywać
GET /jobs/{id}częściej niż raz na pięć sekund —co sekundętrafisz na limit - nie zakładać, że kolejność webhooków odpowiada kolejności wysyłek
- nie trzymać klucza API w kodzie frontendu
- klucz z przeglądarki wycieka natychmiast
- używaj własnego backendu jako pośrednika
Pełna dokumentacja: dokumentacja API oraz cennik. Adres bazowy to https://app.aftertalk.co/api/v1 i nie zmienia się między środowiskami.
Limity
Domyślnie 300 żądań odczytu i 120 żądań zapisu na minutę na klucz. Plik nie może przekraczać 10 MB; dłuższe rozmowy dziel na części.
- Sprawdź nagłówek
Retry-After - Odczekaj wskazany czas
- Ponów żądanie
- przy trzeciej próbie zaloguj błąd
- przy piątej — przerwij i zgłoś incydent
Przy przekroczeniu limitu zwracamy 429 z ciałem {"message":"Rate limit exceeded","retryAfterSeconds":37} — nietypowo długi adres kontrolny wygląda tak: https://app.aftertalk.co/api/v1/projects/8f7e6d5c-4b3a-2910-8f7e-6d5c4b3a2910/calls/jobs/3a2910f7-e6d5-c4b3-a291-08f7e6d5c4b3.
Jeśli potrzebujesz wyższych limitów, napisz na sales@aftertalk.co.