API · PL
API · PL

Pierwsze kroki

WprowadzenieSzybki startUwierzytelnianiePodstawowe pojęcia

Przewodniki

Przesyłanie rozmówPobieranie wynikówWebhookiStronicowanie i wyszukiwanie

Informacje szczegółowe

BłędyLimity żądań i rozliczenia
Dokumentacja referencyjna API
aftertalk.coCennikKontaktEnglish

Webhooki

Otrzymuj powiadomienie, gdy rozmowa jest gotowa, zamiast odpytywać API.

Zarejestruj endpoint, a wyślemy na niego żądanie POST, gdy przetwarzanie rozmowy się zakończy, więc nie musisz odpytywać API.

Wszystko, co opisuje ta strona, możesz też zrobić bez pisania kodu, w sekcji Konfiguracja → Webhooki w projekcie. Ten ekran pozwala rejestrować endpointy, wstrzymywać je i przeglądać opisany niżej dziennik dostaw.

Rejestrowanie endpointu

curl -X POST "$BASE/v1/projects/$PROJECT_ID/webhooks" \
  -H "X-API-Key: $AFTERTALK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your-app.example.com/hooks/aftertalk","events":["call.processed","call.failed"]}'
{
  "id": "3f7c...",
  "url": "https://your-app.example.com/hooks/aftertalk",
  "events": ["call.processed", "call.failed"],
  "active": true,
  "secret": "whsec_9f2c1e...",
  "createdAt": "2026-07-25T10:00:00Z"
}

secret jest zwracany jeden raz, przy tworzeniu. Zapisz go — jest potrzebny do weryfikacji podpisów, a lista webhooków już go nie pokaże.

Adres URL musi być publicznym adresem HTTPS. Zwykłe http, adresy loopback, zakresy prywatne i adresy metadanych chmury są odrzucane z kodem 400 — to samo sprawdzenie jest wykonywane ponownie przed każdą dostawą, więc zmiana rekordu DNS tak, by wskazywał adres wewnętrzny, po rejestracji również nie zadziała.

Zdarzenia

ZdarzenieKiedy
call.processedPrzetwarzanie rozmowy zakończyło się statusem COMPLETED lub BILLING_FAILED, a wyniki zostały zapisane
call.failedPrzetwarzanie zakończyło się statusem FAILED; nie powstała transkrypcja

Treść żądania

Treść żądania jest celowo niewielka — identyfikatory, kategoria i wydźwięk — bez transkrypcji:

{
  "id": "8f14e45f-ea0f-4e21-9c1a-2b3c4d5e6f70",
  "type": "call.processed",
  "createdAt": "2026-07-25T10:04:12Z",
  "data": {
    "callId": "b7e2...",
    "projectId": "1c3a...",
    "jobId": "8f14...",
    "status": "COMPLETED",
    "category": "billing",
    "sentiment": "neutral"
  }
}

callId to ta sama wartość co transcriptId w zadaniu przetwarzania. Przy zdarzeniu call.failed pole errorMessage podaje przyczynę błędu.

Pobierz rozmowę przez GET /calls/{callId}, aby uzyskać transkrypcję i pełną analizę. Dzięki temu, że transkrypcja nie trafia do żądania, nie wysyłamy treści rozmów na Twój adres URL, a Ty zawsze odczytujesz aktualne dane zamiast migawki z chwili wysłania.

status może mieć wartość COMPLETED lub BILLING_FAILED. Obie oznaczają, że wyniki można odczytać — zobacz pobieranie wyników.

Każda dostawa zawiera te nagłówki:

NagłówekZawartość
X-Aftertalk-Signaturet=<unix seconds>,v1=<hmac>
X-Aftertalk-Event-IdIdentyfikator zdarzenia — ta sama wartość co id w treści
X-Aftertalk-Event-Typenp. call.processed

Weryfikacja podpisu

Zawsze weryfikuj podpis, zanim zareagujesz na dostawę — w przeciwnym razie każdy, kto pozna Twój adres URL, może na niego wysyłać żądania.

Podpis to HMAC-SHA256 z "<timestamp>.<raw body>", z Twoim sekretem jako kluczem. Znacznik czasu jest częścią podpisywanego ciągu, więc nie da się go podmienić na świeży; porównaj go z własnym zegarem i odrzucaj zbyt stare żądania.

Node
import crypto from 'node:crypto';

const TOLERANCE_SECONDS = 300;

export function verify(rawBody, header, secret) {
  const parts = new Map(header.split(',').map((p) => p.split('=')));
  const timestamp = Number(parts.get('t'));
  const signature = parts.get('v1');
  if (!timestamp || !signature) return false;

  // Reject replays of an old, otherwise-valid request.
  if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) return false;

  const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');

  // Constant-time compare, so a wrong guess can't be narrowed down by timing.
  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python
import hashlib, hmac, time

TOLERANCE_SECONDS = 300

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    try:
        timestamp = int(parts["t"])
        signature = parts["v1"]
    except (KeyError, ValueError):
        return False

    if abs(time.time() - timestamp) > TOLERANCE_SECONDS:
        return False

    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.".encode() + raw_body,
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(expected, signature)

Weryfikuj na podstawie surowej treści żądania, przed jakimkolwiek parsowaniem JSON i ponowną serializacją. Ponowne kodowanie zmienia bajty, więc podpis nie będzie się zgadzał.

Ponowienia i idempotentność

Dostawę uznaje się za udaną przy dowolnym kodzie 2xx. Wszystko inne — łącznie z przekroczeniem limitu czasu — jest ponawiane z wykładniczo rosnącym odstępem (pierwsze ponowienie po około minucie, każde kolejne po dwukrotnie dłuższej przerwie — łącznie do sześciu prób), po czym dostawa zostaje oznaczona jako nieudana.

To oznacza, że to samo zdarzenie może dotrzeć więcej niż raz. Traktuj X-Aftertalk-Event-Id jako klucz idempotencji i pomijaj zdarzenia, które już obsłużono:

if (await alreadyHandled(eventId)) return res.status(200).end();

Odpowiadaj szybko — zwróć 2xx, gdy tylko zapiszesz zdarzenie, a właściwe przetwarzanie wykonaj później. Limit czasu odpowiedzi wynosi 10 sekund, a zbyt wolna odpowiedź jest traktowana jak błąd.

Aftertalk nie podąża za przekierowaniami: zarejestruj docelowy adres URL.

Debugowanie webhooka, który nie działa

Każda próba jest zapisywana wraz z kodem statusu i błędem:

curl "$BASE/v1/projects/$PROJECT_ID/webhooks/$WEBHOOK_ID/deliveries" \
  -H "X-API-Key: $AFTERTALK_KEY"

Ten sam dziennik znajdziesz w sekcji Konfiguracja → Webhooki w projekcie — wiersz każdego endpointu pokazuje wynik jego ostatniej próby, a przycisk „Historia” otwiera historię dostaw.

Sprawdzaj w tej kolejności:

  1. Brak jakichkolwiek dostaw — czy webhook jest active i czy subskrybuje to zdarzenie? Wstrzymany webhook nic nie otrzymuje.
  2. PENDING — dostawa czeka na wysłanie lub zaplanowane ponowienie.
  3. FAILED z kodem statusu — Twój endpoint odpowiedział kodem innym niż 2xx; responseCode to kod, który zwrócił.
  4. FAILED bez kodu statusu — w ogóle nie udało się z nim połączyć: DNS, TLS, przekroczenie limitu czasu albo adres URL, który nie wskazuje już na adres publiczny. errorMessage podaje przyczynę.

Wstrzymaj webhook na czas naprawy odbiornika, zamiast pozwalać, by próby się wyczerpały:

curl -X PATCH "$BASE/v1/projects/$PROJECT_ID/webhooks/$WEBHOOK_ID/toggle" \
  -H "X-API-Key: $AFTERTALK_KEY"

Pobieranie wyników

Śledź zadanie przetwarzania do końca i odczytaj analizę.

Stronicowanie i wyszukiwanie

Przeglądanie rozmów strona po stronie, filtrowanie po dacie i wyszukiwanie pełnotekstowe.

Na tej stronie

Rejestrowanie endpointuZdarzeniaTreść żądaniaWeryfikacja podpisuPonowienia i idempotentnośćDebugowanie webhooka, który nie działa