Przejdź do treści

Dla deweloperów

Połącz Telofia z systemami, których już używasz

Pobieraj rozmowy, transkrypcje, wizyty i kontakty przez REST API, odbieraj podpisane webhooki w chwili, gdy coś się dzieje, pozwól asystentowi sprawdzać dane w Twoim systemie w trakcie rozmowy i dodaj asystenta głosowego na stronę jedną linijką kodu.

REST API i webhooki są w każdym pakiecie, od Line; wyższe pakiety mają wyższe limity. W okresie próbnym masz dostęp testowy: 1 klucz API, 20 zapytań na minutę.

Adres bazowy API
https://telofia.com/api/v1
curl · Test klucza
curl https://telofia.com/api/v1/me \
  -H "Authorization: Bearer tf_live_…"
200 OK
{
  "object": "account",
  "id": "1f2e3d4c-5b6a-4789-8a7b-6c5d4e3f2a1b",
  "name": "Smile Dental",
  "timezone": "Europe/Warsaw",
  "locale": "en",
  "country": "PL",
  "currency": "PLN",
  "status": "active",
  "plan": {
    "id": "team",
    "billing_cycle": "month",
    "trial_ends_at": null,
    "period_start": "2026-10-01T00:00:00Z",
    "period_end": "2026-11-01T00:00:00Z",
    "recording_history_days": 365
  },
  "minutes": {
    "included": 3000,
    "extra": 100,
    "total": 3100,
    "used": 412.5,
    "remaining": 2687.5
  },
  "api_key_id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "scopes": [
    "read:calls",
    "read:appointments",
    "read:contacts",
    "read:tasks"
  ],
  "limits": {
    "plan": "team",
    "requests_per_minute": 180,
    "requests_per_day": 50000,
    "max_api_keys": 10,
    "max_webhook_endpoints": 10
  }
}

Szybki start

Pierwsze zapytanie w dwie minuty

  1. 1

    Utwórz klucz API

    W panelu wejdź w Dla deweloperów → Klucze API, nazwij klucz i wybierz jego uprawnienia. Klucz (tf_live_…) zobaczysz tylko raz, więc od razu zapisz go w menedżerze sekretów.

  2. 2

    Sprawdź połączenie

    Wywołaj GET /me. Działa z każdym ważnym kluczem i zwraca Twoje konto, uprawnienia klucza i limity pakietu.

  3. 3

    Pobieraj dane albo subskrybuj zdarzenia

    Pobieraj listy rozmów, wizyt i kontaktów albo dodaj punkt odbioru w Dla deweloperów → Webhooki, aby dostawać zdarzenia na bieżąco.

curl · Test klucza
curl https://telofia.com/api/v1/me \
  -H "Authorization: Bearer tf_live_…"
JavaScript: wszystkie rozmowy z rezerwacją, strona po stronie
const API = "https://telofia.com/api/v1";
const headers = { Authorization: `Bearer ${process.env.TELOFIA_API_KEY}` };

// All calls booked since 1 October, page by page
let cursor = null;
while (true) {
  const url = new URL(`${API}/calls`);
  url.searchParams.set("outcome", "booked");
  url.searchParams.set("since", "2026-10-01T00:00:00Z");
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("cursor", cursor);

  const res = await fetch(url, { headers });
  if (res.status === 429) {
    // rate limited: wait for the number of seconds in Retry-After, then retry the same page
    await new Promise((r) => setTimeout(r, Number(res.headers.get("Retry-After") ?? 1) * 1000));
    continue;
  }
  if (!res.ok) throw new Error((await res.json()).error.message);

  const page = await res.json();
  for (const call of page.data) console.log(call.started_at, call.from, call.summary);
  if (!page.has_more) break;
  cursor = page.next_cursor;
}

REST API v1

Rozmowy, wizyty i kontakty przez HTTPS

JSON na wejściu i na wyjściu. Każde zapytanie uwierzytelnia klucz API, a limity zależą od pakietu. Zapytania widać w panelu (metoda, ścieżka, status i czas — nigdy treść zapytań ani odpowiedzi).

Uwierzytelnianie

Utwórz klucz w zakładce Klucze API i przesyłaj go jako token Bearer. Klucze zaczynają się od tf_live_ i należą do jednego konta.

Authorization: Bearer tf_live_…

Stronicowanie

Endpointy list zwracają maksymalnie limit elementów (1–100, domyślnie 25) w stałej kolejności. Gdy has_more ma wartość true, przekaż niezmieniony next_cursor jako cursor (z tymi samymi filtrami), aby pobrać następną stronę. Niepoprawny kursor zwraca 400 invalid_request.

Błędy

Błędy są zwracane ze standardowymi kodami statusu HTTP i treścią JSON zawierającą typ błędu oraz czytelny komunikat.

{ "error": { "type": "…", "message": "…" } }

Punkty końcowe

  • GET/meUprawnienie: każdy klucz

    Pobierz bieżące konto

    Zwraca konto, do którego należy klucz API. Przydatne jako wywołanie „test połączenia”.

  • GET/assistantsUprawnienie: każdy klucz

    Lista asystentów

    Twoi asystenci z językiem, stanem i przypisanymi numerami telefonów. Identyfikator możesz podać jako assistant_id przy filtrowaniu rozmów.

  • GET/assistants/{id}Uprawnienie: każdy klucz

    Pobierz asystenta

    Jeden asystent z językiem, stanem i numerami telefonów. Z zakresem read:assistants (lub write:assistants) zwraca też prompt: instructions, greeting, company_profile i updated_at.

  • PATCH/assistants/{id}Uprawnienie: write:assistants

    Zmień prompt asystenta

    Zmienia instrukcje, powitanie lub profil firmy. Pominięte pola zostają bez zmian. Te same limity co w panelu: instrukcje do 20 000 znaków, profil firmy do 10 000, powitanie 5–600 znaków i musi informować, że rozmówca rozmawia z AI (inaczej 400 z kodem ai_disclosure). Każda zmiana trafia do historii wersji asystenta (źródło API) i obowiązuje od następnej rozmowy. Zasady uczciwości platformy (informacja o AI i nagrywaniu) zawsze obowiązują niezależnie od instrukcji.

  • GET/phone-numbersUprawnienie: każdy klucz

    Lista numerów telefonów

    Numery telefonów konta z krajem, rodzajem, stanem i asystentem, który je odbiera.

  • GET/callsUprawnienie: read:calls

    Lista rozmów

    Rozmowy od najnowszych, bez transkrypcji. Rozmowy demo nigdy nie są uwzględniane.

  • GET/calls/{id}Uprawnienie: read:calls

    Pobierz rozmowę

    Pojedyncza rozmowa z pełną transkrypcją i wizytami umówionymi w jej trakcie.

  • GET/calls/{id}/recordingUprawnienie: read:calls

    Pobierz nagranie rozmowy

    Podpisane linki (ważne 1 godzinę) do odsłuchu i pobrania nagrania MP3 (stereo: dzwoniący po lewej, asystent po prawej). Nagranie starsze niż historia nagrań w Twoim pakiecie zwraca 403 plan_required z polem recording_history_days; rozmowa bez nagrania zwraca 404.

  • GET/appointmentsUprawnienie: read:appointments

    Lista wizyt

    Nadchodzące wizyty w kolejności rozpoczęcia (domyślnie od chwili obecnej).

  • GET/appointments/{id}Uprawnienie: read:appointments

    Pobierz wizytę

    Jedna wizyta Twojego konta.

  • POST/appointments/{id}/cancelUprawnienie: write:appointments

    Odwołaj wizytę

    Odwołuje nadchodzącą wizytę, usuwa ją z podłączonego kalendarza Google i wysyła webhook appointment.canceled. Pole calendar_sync mówi, czy kalendarz został zaktualizowany.

  • POST/appointments/{id}/rescheduleUprawnienie: write:appointments

    Przełóż wizytę

    Przenosi nadchodzącą wizytę na nowy termin. Zwraca 409, gdy termin koliduje z inną wizytą lub zajętym czasem w podłączonym kalendarzu. Bez ends_at długość wizyty się nie zmienia. Wysyła webhook appointment.rescheduled.

  • GET/contactsUprawnienie: read:contacts

    Lista kontaktów

    Osoby, które do Ciebie dzwoniły, od najnowszych. Asystent pamięta je między rozmowami.

  • GET/contacts/{id}Uprawnienie: read:contacts

    Pobierz kontakt

    Jeden kontakt z imieniem, e-mailem, tagami, notatkami i źródłem imienia oraz e-maila (call, system lub manual).

  • POST/contactsUprawnienie: write:contacts

    Utwórz kontakt

    Dodaje kontakt, np. z CRM, aby asystent znał imię dzwoniącego. Imię i e-mail zapisane przez API mają źródło manual i asystent nigdy ich nie nadpisze. Zwraca 409 z contact_id, gdy numer już istnieje. Wysyła webhook contact.created.

  • PATCH/contacts/{id}Uprawnienie: write:contacts

    Zmień kontakt

    Zmienia imię, e-mail, notatki lub blokadę. Pominięte pola zostają bez zmian, a null czyści pole. Zmienione imię lub e-mail dostaje źródło manual, więc asystent go nie nadpisze. Numeru telefonu nie można zmienić.

  • GET/tasksUprawnienie: read:tasks

    Lista zadań

    Prośby o oddzwonienie, wiadomości i inne zadania z rozmów, od najnowszych.

  • GET/tasks/{id}Uprawnienie: read:tasks

    Pobierz zadanie

    Jedno zadanie Twojego konta.

  • PATCH/tasks/{id}Uprawnienie: write:tasks

    Zmień zadanie

    Ustawia status zadania: open, done lub dismissed, np. gdy Twój zespół oddzwonił już do klienta.

  • POST/calls/outboundUprawnienie: write:calls

    Utwórz połączenie wychodzące

    Asystent dzwoni do jednej osoby z numeru Twojej firmy z podanym przez Ciebie celem — tak jak krok sekwencji onboardingowej. Połączenie wychodzi od razu albo o call_at, zawsze w godzinach dzwonienia (pon.–sob., 9:00–20:00 w strefie czasowej Twojego konta; poza nimi przesuwa się na najbliższy dozwolony termin, a call_at pokazuje, kiedy). Gdy nikt nie odbiera, asystent próbuje do 3 razy, co 2 godziny. Zwraca 202 z połączeniem w statusie scheduled. Błędy: 400 consent_missing, invalid_phone, number_not_callable, international lub invalid_call_at; 409 opted_out (osoba nie chce telefonów), already_scheduled (z existing_id), assistant_not_live, over_quota lub no_outbound_number; 429 daily_outbound_limit z limit (domyślnie 200 połączeń na 24 godziny). Wynik przychodzi w webhookach call.completed i outbound_call.finished z Twoim external_id.

  • GET/calls/outbound/{id}Uprawnienie: write:calls

    Pobierz połączenie wychodzące

    Status zaplanowanego połączenia wychodzącego: scheduled, dialing, done (z call_id rozmowy), failed (last_error, np. no_answer po wszystkich próbach) lub canceled. Działa też z callback_id z webhooków dla połączeń onboardingowych i oddzwonień.

  • DELETE/calls/outbound/{id}Uprawnienie: write:calls

    Anuluj połączenie wychodzące

    Anuluje utworzone przez API połączenie wychodzące, które jest jeszcze zaplanowane. Można bezpiecznie powtarzać (już anulowane zwraca 200). Zwraca 409 not_cancelable, gdy trwa wybieranie numeru albo połączenie się zakończyło, a także dla połączeń onboardingowych (zamiast tego wypisz osobę przez POST /api/outreach/unenroll).

  • GET/messagesUprawnienie: read:messages

    Lista SMS-ów

    SMS-y wysłane przez konto i odpowiedzi klientów, od najnowszych. phone to numer klienta (odbiorca albo nadawca odpowiedzi). generated_by: template, ai albo manual; charged_from: plan albo credits.

  • POST/messagesUprawnienie: write:messages

    Wyślij SMS

    Wysyła SMS do Twojego kontaktu lub dzwoniącego: własny tekst albo szablon konta ze zmiennymi vars (date, time, name, service, link, amount). Wlicza się do limitu SMS pakietu i dokupionych SMS-ów. Błędy: 400 invalid_request (consent_missing, invalid_phone, too_long, missing_vars); 409 conflict (opted_out — klient odpisał STOP, over_quota, recipient_not_allowed — najpierw utwórz kontakt, blocked_destination); 429 rate_limited.

Uprawnienia

Każdy klucz ma uprawnienia wybrane przy jego tworzeniu. Uprawnień nie można później zmienić — gdy potrzebujesz więcej, utwórz nowy klucz. Gdy kluczowi brakuje wymaganego uprawnienia, endpoint odpowiada 403 insufficient_scope. GET /me, /assistants i /phone-numbers działają z każdym ważnym kluczem.

  • read:callsOdczyt rozmów, podsumowań i transkrypcji
  • read:appointmentsOdczyt wizyt
  • write:appointmentsOdwoływanie i przekładanie wizyt
  • read:contactsOdczyt kontaktów
  • write:contactsTworzenie i edycja kontaktów
  • read:tasksOdczyt zadań i próśb o oddzwonienie
  • write:tasksZmiana statusu zadań
  • write:callsUruchamianie połączeń wychodzących, sprawdzanie i anulowanie ich
  • read:messagesOdczyt SMS-ów i odpowiedzi klientów
  • write:messagesWysyłanie SMS-ów do Twoich kontaktów
  • read:assistantsOdczyt promptu asystenta (instrukcje, powitanie, profil firmy)
  • write:assistantsZmiana promptu asystenta

Błędy

  • 400invalid_request — brakuje parametru lub ma on niepoprawną wartość
  • 401unauthorized — brak klucza API, klucz jest niepoprawny lub został unieważniony
  • 403insufficient_scope — klucz nie ma uprawnienia endpointu; plan_required — konto nie ma aktywnego pakietu albo nagranie jest starsze niż historia nagrań w pakiecie
  • 404not_found — obiekt nie istnieje na Twoim koncie
  • 409conflict — termin jest zajęty, wizyty nie można zmienić, kontakt z tym numerem już istnieje albo nie można wykonać połączenia wychodzącego (code: opted_out, already_scheduled, assistant_not_live, over_quota, no_outbound_number, not_cancelable)
  • 429rate_limited — za dużo zapytań; ponów po czasie z nagłówka Retry-After
  • 500server_error — wystąpił błąd po naszej stronie
  • 503service_unavailable — chwilowy problem; ponów po czasie z nagłówka Retry-After

Pełna dokumentacja API z parametrami i przykładowymi odpowiedziami jest w panelu: Dla deweloperów → Dokumentacja API.

Webhooki

Zdarzenia wysyłane na Twój serwer w czasie rzeczywistym

Dodaj w panelu adres https, wybierz zdarzenia, a my wyślemy podpisaną kopertę JSON (POST), gdy tylko coś się wydarzy. „Wyślij test” dostarcza przykład pierwszego wybranego zdarzenia — przydaje się do mapowania pól w Zapierze czy Make.

Zdarzenia

  • call.completedRozmowa zakończona

    Podsumowanie, wynik, nastawienie, transkrypcja, kontakt i rezerwacje; przy połączeniach wychodzących także sekwencja lub żądanie API (callback_id, external_id, outreach).

  • call.startedRozmowa rozpoczęta

    Odebrano rozmowę (przychodzącą, wychodzącą lub przez przeglądarkę).

  • appointment.bookedUmówiono wizytę

    Asystent umówił wizytę.

  • appointment.canceledOdwołano wizytę

    Wizyta została odwołana telefonicznie lub przez API.

  • appointment.rescheduledPrzełożono wizytę

    Wizyta została przeniesiona na nowy termin (z poprzednim terminem).

  • task.createdUtworzono oddzwonienie / zadanie

    Dzwoniący poprosił o oddzwonienie lub zostawił zadanie.

  • contact.createdNowy kontakt

    Osoba dzwoniąca po raz pierwszy została zapisana jako kontakt.

  • outbound_call.finishedPołączenie wychodzące zakończone

    Zaplanowane połączenie wychodzące (API, sekwencja onboardingowa lub oddzwonienie) zakończyło się albo nie powiodło — także gdy nikt nie odebrał po wszystkich próbach.

Nagłówki każdej dostawy

  • Telofia-SignaturePodpis: t=<czas unix>,v1=<HMAC-SHA256 hex>
  • Telofia-EventTyp zdarzenia, np. appointment.booked
  • Telofia-DeliveryUnikalny identyfikator dostawy
  • User-AgentTelofia-Webhooks/1.0 · Identyfikuje nadawcę webhooków

Dostarczanie i ponowienia

  • Odpowiedz dowolnym statusem 2xx w ciągu 10 sekund. Przekierowania nie są obsługiwane.
  • Nieudane dostawy ponawiamy po 1 min, 5 min, 30 min, 2 h, 6 h i 12 h (7 prób, ok. 21 godzin).
  • To samo zdarzenie może przyjść więcej niż raz. Duplikaty pomijaj po polu id zdarzenia.
  • Przyjmujemy tylko publiczne adresy https. Po 50 nieudanych próbach z rzędu punkt odbioru jest wstrzymywany, a panel pokazuje powód.
  • Panel prowadzi dziennik dostaw z kodami statusu i odpowiedziami i pozwala ponowić dostawę ręcznie.
Przykładowa dostawa: call.completed
{
  "id": "evt_3f9a1c7e5b2d4f6a8c0e1b3d5f7a9c1e",
  "type": "call.completed",
  "created_at": "2026-10-01T09:17:45Z",
  "account_id": "1f2e3d4c-5b6a-4789-8a7b-6c5d4e3f2a1b",
  "data": {
    "id": "5b1f0c6e-2a8d-4f3e-9d51-7c0a4e2b9f10",
    "object": "call",
    "assistant_id": "0e6f4c8a-3b2d-4a1e-8f7c-5d9b2a1c3e4f",
    "phone_number_id": "4d3c2b1a-0f9e-4d8c-b7a6-958473625140",
    "contact_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
    "direction": "inbound",
    "status": "completed",
    "from": "+48601234567",
    "to": "+48221234567",
    "started_at": "2026-10-01T09:14:03Z",
    "ended_at": "2026-10-01T09:17:41Z",
    "duration_sec": 218,
    "summary": "Anna Kowalska booked a first consultation for Friday 10:00.",
    "outcome": "booked",
    "sentiment": "positive",
    "tags": [
      "new-customer"
    ],
    "has_recording": true,
    "end_reason": "caller_hangup",
    "callback_id": null,
    "external_id": null,
    "outreach": null,
    "caller_spoke": true,
    "caller_words": 6,
    "transcript": [
      {
        "role": "caller",
        "text": "I'd like to book a consultation this week.",
        "at": 4.1
      }
    ],
    "contact": {
      "id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
      "object": "contact",
      "phone": "+48601234567",
      "name": "Anna Kowalska",
      "name_source": "call",
      "email": "anna.kowalska@example.com",
      "email_source": "call",
      "notes": null,
      "tags": [
        "customer"
      ],
      "blocked": false,
      "calls_count": 3,
      "last_call_at": "2026-10-01T09:14:03Z",
      "created_at": "2026-08-12T15:02:11Z"
    },
    "appointments": [
      {
        "id": "c3d2e1f0-a9b8-4c7d-8e6f-5a4b3c2d1e0f",
        "object": "appointment",
        "call_id": "5b1f0c6e-2a8d-4f3e-9d51-7c0a4e2b9f10",
        "contact_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
        "customer_name": "Anna Kowalska",
        "customer_phone": "+48601234567",
        "customer_email": "anna.kowalska@example.com",
        "service": "First consultation",
        "starts_at": "2026-10-03T08:00:00Z",
        "ends_at": "2026-10-03T08:30:00Z",
        "status": "booked",
        "external_id": "google:7h3k9s2l1m0n",
        "created_at": "2026-10-01T09:16:20Z"
      }
    ]
  }
}

Weryfikacja podpisów. Każde dostarczenie zawiera nagłówek Telofia-Signature w formacie t=timestamp,v1=signature. Oblicz HMAC-SHA256 z „timestamp.raw_body” przy użyciu swojego sekretu podpisu i porównaj wynik z v1. Odrzucaj znaczniki czasu starsze niż 5 minut.

Node.js · Weryfikacja podpisu
import crypto from "node:crypto";

// Express: app.post("/webhook", express.raw({ type: "application/json" }), handler)
export function verify(rawBody, signatureHeader, secret) {
  // Telofia-Signature: t=1727774265,v1=5f2c…
  const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const given = Buffer.from(parts.v1 ?? "");
  // timingSafeEqual needs buffers of the same length
  return fresh && given.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), given);
}
Python · Weryfikacja podpisu
import hmac, hashlib, time

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in signature_header.split(","))
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
    fresh = abs(time.time() - int(parts["t"])) < 300
    return fresh and hmac.compare_digest(expected, parts.get("v1", ""))
Przykładowy odbiornik (Express)
import express from "express";
import { verify } from "./verify.js";

const app = express();
const seen = new Set(); // use your database in production

app.post("/telofia/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const raw = req.body.toString("utf8");
  if (!verify(raw, req.get("Telofia-Signature") ?? "", process.env.TELOFIA_WEBHOOK_SECRET)) return res.sendStatus(401);

  const event = JSON.parse(raw);
  if (seen.has(event.id)) return res.sendStatus(200); // retried delivery, already handled
  seen.add(event.id);

  if (event.type === "appointment.booked") {
    const a = event.data;
    console.log("New booking:", a.customer_name, a.service, a.starts_at);
  }
  res.sendStatus(200); // answer with 2xx within 10 seconds
});

app.listen(3000);

Stałe wartości

Te wartości w rozmowach (API i webhooki call.*) oraz w połączeniach wychodzących (outbound_call.finished, /calls/outbound) są stałym kontraktem: możemy dodawać nowe wartości, ale nie zmienimy ani nie usuniemy tych poniżej. Nieodebrane, zajęte lub odrzucone próby połączeń wychodzących nigdy nie tworzą rozmowy, więc nie ma dla nich call.completed: po ostatniej próbie dostaniesz outbound_call.finished z last_error no_answer. Połączenie odebrane przez pocztę głosową liczy się jako odebrane (zwykle krótka rozmowa z wynikiem other).

Status rozmowy

in_progress
Rozmowa trwa.
completed
Rozmówca rozmawiał z asystentem.
missed
Nikt się nie odezwał albo asystent nie mógł odebrać (zobacz end_reason).
failed
Nie udało się obsłużyć rozmowy z powodu błędu.

Wynik rozmowy

booked
Umówiono wizytę.
transferred
Rozmowa została przekazana do człowieka.
message
Przyjęto wiadomość, prośbę o oddzwonienie, zamówienie albo sprawę do załatwienia.
info
Rozmówca otrzymał informacje; nic więcej nie było potrzebne.
spam
Spam, telemarketing albo zablokowany numer.
other
Wszystko inne, np. bardzo krótka rozmowa albo poczta głosowa.

Powód zakończenia rozmowy (end_reason)

caller_hangup
Rozmówca się rozłączył.
caller_hangup_greeting
Połączenie wychodzące: rozmówca rozłączył się w trakcie powitania albo zaraz po nim, nie mówiąc ani słowa (rozmowa do 45 s). Wtedy caller_spoke = false.
agent_hangup
Asystent zakończył rozmowę po pożegnaniu.
transferred
Rozmowa została przekazana do człowieka.
silence
Zakończona po długiej ciszy.
max_duration
Osiągnięto maksymalną długość rozmowy.
spam
Zakończona jako spam.
blocked
Numer jest zablokowany w kontaktach.
busy
Wszystkie linie konta były zajęte.
over_quota
Brak minut na rozmowy.
trial_expired
Bezpłatny okres próbny się zakończył.
inactive
Brak aktywnego pakietu.
assistant_not_live
Asystent jest wstrzymany.
no_assistant
Żaden asystent nie odbiera tego numeru.
ai_budget
Obsłużona bez AI z powodu chwilowego limitu; zespół dostał prośbę o oddzwonienie.
error
Rozmowę zakończył błąd techniczny.

Status połączenia wychodzącego

scheduled
Czeka na swój termin (albo na kolejną próbę).
dialing
Trwa wybieranie numeru.
done
Odebrane; call_id wskazuje rozmowę.
failed
Nieodebrane po wszystkich próbach albo nie udało się go wykonać (zobacz last_error).
canceled
Anulowane (przez API, wypisanie osoby z sekwencji albo wyłączenie sekwencji).

last_error połączenia wychodzącego

no_answer
Nikt nie odebrał (także zajęte lub odrzucone).
failed
Asystent nie był dostępny w chwili połączenia (pakiet, minuty albo wstrzymanie).
no_result
Brak wyniku połączenia w ciągu 15 minut.
expired
Połączenie było opóźnione o ponad 2 godziny, więc nie zostało wykonane.
outside_window
Przeniesione na kolejne godziny dzwonienia (status pozostaje scheduled).
dial_error
Sieć telefoniczna odrzuciła połączenie (wysyłane jako dial_<reason>).
over_quota
Brak minut na rozmowy.
inactive
Brak aktywnego pakietu.
assistant_not_live
Asystent jest wstrzymany.
no_outbound_number
Brak numeru firmy, z którego można dzwonić.
canceled_by_api
Anulowane przez DELETE /calls/outbound/{id}.
unenrolled
Osoba została wypisana z sekwencji (POST /api/outreach/unenroll).
opt_out
Osoba poprosiła, żeby do niej nie dzwonić.
replaced
Zastąpione nowszym oddzwonieniem pod ten sam numer.

Dane na żywo przez MCP

Asystent sprawdza dane w Twoim systemie w trakcie rozmowy

Podłącz zdalny serwer MCP (Model Context Protocol), np. katalog sklepu, stany magazynowe, system rezerwacji albo zamówień. Sam decydujesz, z których narzędzi i zasobów (tylko do odczytu) może korzystać asystent.

Naucz się z wyprzedzeniem

Dla treści, które rzadko się zmieniają: cenników, opisów produktów, regulaminów. Wybrane zasoby i narzędzia tylko do odczytu trafiają do wiedzy asystenta co 1–168 godzin albo na żądanie. Zero opóźnień w rozmowie.

Sprawdzaj na żywo w rozmowie

Dla rzeczy, które się zmieniają: stanów magazynowych, dostępności, statusu zamówienia. Asystent wywołuje narzędzie w trakcie rozmowy z krótkim „chwileczkę, już sprawdzam”. Jeśli serwer nie odpowie w ok. 2,5 sekundy, asystent mówi, że nie może tego teraz potwierdzić, i proponuje kontakt od zespołu.

Czego potrzebujesz

  • Serwera MCP dostępnego przez https (Streamable HTTP, np. https://mcp.example.com/mcp; starsze serwery HTTP+SSE wykrywamy automatycznie).
  • Uwierzytelnienia: brak, token Bearer albo własny nagłówek, np. X-API-Key. Sekrety są szyfrowane (AES-256-GCM) i nigdy nie trafiają do przeglądarki.
  • Narzędzi tylko do odczytu. Narzędzia oznaczone jako niszczące są blokowane; przy narzędziach bez oznaczenia „tylko do odczytu” potwierdzasz, że jedynie czytają dane.
  • Do 5 źródeł i 50 narzędzi na żywo na asystenta. Sprawdzenie na żywo powinno trwać do ok. 2 sekund.
  • Opcjonalnie dla widżetu na stronie: w narzędziu „Na początku rozmowy” wybierz argument, do którego trafia token tożsamości zalogowanego użytkownika (zob. Widżet głosowy na stronę).

Argumenty narzędzi są sprawdzane względem schematu narzędzia, wyniki są skracane i traktowane wyłącznie jako dane (nigdy jako polecenia), a każde sprawdzenie trafia do dziennika bez danych dzwoniącego. Adresy prywatne i wewnętrzne są odrzucane.

Widżet głosowy na stronę

Twój asystent na Twojej stronie — jedna linijka kodu

Odwiedzający klika przycisk i rozmawia w przeglądarce z tym samym asystentem, który odbiera Twój telefon. Rozmowy z widżetu zużywają minuty pakietu, bez kosztów telefonii.

Od pakietu Office

  • Ok. 14 KB, bez zależności, izolowany w Shadow DOM, więc style Twojej strony go nie zepsują.
  • Mówi w języku Twojej strony (<html lang>) albo w tym, który ustawisz atrybutem data-lang="de".
  • Napisy na żywo; gdy mikrofon jest zablokowany, odwiedzający może pisać, a asystent odpowiada głosem.
  • Możesz ograniczyć widżet do swoich domen — wtedy uruchomi się tylko na tych stronach.
Kod do wklejenia (klucz znajdziesz w panelu)
<script src="https://telofia.com/widget.js" data-key="tfw_…" async></script>
API JavaScript
// optional: open the widget from your own button
document.querySelector("#talk-to-us").addEventListener("click", () => window.TelofiaWidget?.open());

window.TelofiaWidget?.close();   // close the panel
window.TelofiaWidget?.destroy(); // remove the widget from the page

Masz restrykcyjne Content Security Policy? Dopuść script-src https://telofia.com https://cdn.jsdelivr.net, connect-src https://telofia.com wss://*.livekit.cloud https://*.livekit.cloud oraz media-src blob:.

Zalogowani użytkownicy (token tożsamości)

Gdy odwiedzający jest zalogowany na Twojej stronie, asystent może wiedzieć, kto mówi, bez pytania o e-mail.

  • Twój serwer wydaje zalogowanemu użytkownikowi krótkotrwały, podpisany token (np. HMAC, ważny 15 minut). Nigdy nie umieszczaj w przeglądarce adresu e-mail ani identyfikatora użytkownika: każdy mógłby wpisać w konsoli cudzy.
  • Wywołaj TelofiaWidget.identify({ token }) w dowolnej chwili; kolejne wywołanie podmienia token, a identify(null) go czyści. Zanim widget.js się załaduje, ustaw window.TelofiaIdentity = { token, expires_at }: jest odczytywany przy starcie rozmowy, a wygasły token jest pomijany.
  • W panelu wybierz argument z tokenem tożsamości w narzędziu MCP „Na początku rozmowy”. Telofia przekazuje token bez zmian wyłącznie do tego argumentu, nigdy go nie zapisuje ani nie pokazuje i nie trafia on do transkrypcji, podsumowań, webhooków ani logów; asystent go nie widzi.
  • Od 8 do 400 znaków: litery, cyfry oraz . _ ~ + / = - (np. base64url). Odświeżaj go przed wygaśnięciem, np. co 10 minut. Sprawdza go Twój serwer MCP; zły albo wygasły token powinien dawać taki wynik jak brak tokenu. Rozmowy telefoniczne i rozmowy bez tokenu działają jak dotąd.
Token tożsamości
// signed-in user: a short-lived, signed token issued by YOUR server (never an e-mail or user ID)
const { token, expires_at } = await fetch("/api/telofia-identity").then((r) => r.json());

window.TelofiaIdentity = { token, expires_at };       // read when the conversation starts, also before widget.js loads
window.TelofiaWidget?.identify({ token, expires_at }); // replaces the token at any time

// refresh before it expires (e.g. every 10 minutes); on sign-out:
window.TelofiaIdentity = null;
window.TelofiaWidget?.identify(null);

Onboarding telefoniczny

Nowi klienci automatycznie dostają powitalny telefon

Gdy ktoś zarejestruje się w Twoim systemie, przekaż go do sekwencji połączeń. Asystent dzwoni z numeru Twojej firmy, pomaga zacząć, a po kilku dniach sprawdza, czy wszystko działa. Dzwonimy w godzinach dzwonienia sekwencji: domyślnie od poniedziałku do soboty, 9:00–20:00 w Twojej strefie czasowej, albo w dni i godziny, które ustawisz.

POST/api/outreach/enroll

Uwierzytelnienie kluczem sekwencji (tlo_…) z panelu — jako token Bearer albo w nagłówku X-Telofia-Key. Każda sekwencja ma własny klucz.

Treść zapytania

  • phonestringwymagany
    Numer telefonu, najlepiej w formacie E.164. Numery krajowe odczytujemy z kierunkowym kraju Twojego konta.
  • consenttruewymagany
    Musi mieć wartość true: osoba zgodziła się na kontakt telefoniczny, np. w Twoim formularzu rejestracji.
  • namestringopcjonalny
    Imię i nazwisko, do 120 znaków.
  • emailstringopcjonalny
    Adres e-mail.
  • contextstringopcjonalny
    Co asystent powinien wiedzieć o tej osobie, do 1000 znaków (np. wybrany pakiet).
  • external_idstring | numberopcjonalny
    Identyfikator osoby w Twoim systemie (tekst lub liczba). Ten sam identyfikator zapisujemy tylko raz.
curl
curl -X POST "https://telofia.com/api/outreach/enroll" \
  -H "Authorization: Bearer tlo_…" \
  -H "Content-Type: application/json" \
  -d '{"phone":"+48601234567","name":"Anna Kowalska","email":"anna@example.com","context":"Signed up today, Start plan","external_id":"user_123","consent":true}'
201 Created
{
  "ok": true,
  "enrollment_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
  "external_id": "user_123",
  "status": "active",
  "next_call_at": "2026-10-09T10:20:00Z",
  "limits": {
    "per_day": 500,
    "remaining_today": 499
  }
}

Odpowiedzi

  • 201: zapisano. enrollment_id, external_id, status, next_call_at (pora pierwszego telefonu), intro_sms_at (kiedy wyjdzie SMS przed pierwszym telefonem albo null) i limits { per_day, remaining_today }
  • 200: osoba jest już zapisana (ten sam external_id albo ten numer jest w toku w sekwencji), z duplicate: true
  • 400: invalid_request, invalid_phone lub consent_required
  • 401: invalid_key
  • 409: sequence_disabled, consent_missing lub opted_out (osoba nie chce telefonów)
  • 429: limit — wyczerpany dzienny limit zapisów do sekwencji (500 nowych osób na 24 godziny; treść zawiera limit). rate_limited: więcej niż 120 zapytań na minutę z tym kluczem (nagłówek Retry-After)

Godziny dzwonienia: w panelu ustawisz dla każdej sekwencji dni i przedział godzin między 7:00 a 21:00. Gdy nikt nie odbiera, asystent próbuje do 3 razy, co 2 godziny, w tych godzinach.

SMS przed telefonem: gdy jest włączony w sekwencji (panel), osoba dostaje krótką wiadomość z numeru, z którego zadzwoni asystent — domyślnie 20 minut przed pierwszym telefonem (5–120 min). Jeśli pierwszy telefon wypadłby wcześniej, przesuwamy go, żeby SMS zawsze wyszedł pierwszy, w godzinach dzwonienia. Nieudany SMS (osoba odpisała STOP, wyczerpany limit SMS) nigdy nie wstrzymuje telefonu. Każdy SMS wlicza się do limitu SMS i jest widoczny w panelu w zakładce SMS.

Powitanie: w panelu ustawisz dla każdego kroku (albo domyślnie dla całej sekwencji) pierwsze zdanie rozmowy — stały tekst ze zmiennymi {first_name}, {first_name_vocative} (wołacz po polsku, np. Krystianie), {assistant_name}, Telofia (nazwa firmy w rozmowie z ustawień asystenta) i {name}, z osobnym wariantem, gdy w zapisie nie ma name — albo tryb, w którym pierwszą wypowiedź układa asystent z name, context, celu kroku i wyniku narzędzi „Na początku rozmowy”. Informację, że dzwoni asystent AI, i o nagrywaniu zawsze dokładamy w pierwszej wypowiedzi, jeśli tekst ich nie zawiera.

Wypisz osobę z sekwencji

POST/api/outreach/unenroll

Gdy ktoś nie potrzebuje już telefonów (np. po pierwszej sprzedaży albo gdy zrezygnował), zatrzymaj jego sekwencję tym samym kluczem. Zaplanowane rozmowy są od razu anulowane. Trwająca rozmowa nie zostanie przerwana, ale po niej nie będzie kolejnych.

  • external_id | enrollment_id | phonewymagany
    Dokładnie jedno z: external_id (jak przy zapisie), enrollment_id (z odpowiedzi na zapis) albo phone.
  • reasonopcjonalny
    Opcjonalny powód, do 200 znaków, zapisywany w dzienniku zdarzeń Twojego konta.
  • 200: zatrzymano (changed: true) albo już zakończono: status stopped, completed lub failed (changed: false). Można bezpiecznie powtarzać.
  • 400: invalid_request (brak identyfikatora albo więcej niż jeden) lub invalid_phone
  • 401: invalid_key
  • 404: not_found — w tej sekwencji nie ma takiego zapisu
  • 429: rate_limited
curl
curl -X POST "https://telofia.com/api/outreach/unenroll" \
  -H "Authorization: Bearer tlo_…" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"user_123","reason":"first_sale"}'
200 OK
{
  "ok": true,
  "enrollment_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
  "external_id": "user_123",
  "status": "stopped",
  "changed": true
}

Wyniki rozmów w webhookach

call.started i call.completed dla rozmów z sekwencji zawierają callback_id, external_id oraz outreach { sequence_id, enrollment_id, external_id, step } (dla rozmów przychodzących wszystkie mają wartość null). Nieodebrane próby nie tworzą rozmowy, więc nie ma dla nich call.completed: po ostatniej próbie dostaniesz outbound_call.finished ze statusem failed i last_error no_answer. Ponowne próby jednego kroku liczą się jako jeden wynik. call.completed zawiera też caller_spoke i caller_words (ile słów powiedział rozmówca). Gdy ktoś odebrał i rozłączył się w trakcie powitania albo zaraz po nim bez słowa, end_reason ma wartość caller_hangup_greeting.

call.completed dla rozmowy z sekwencji
{
  "id": "5b1f0c6e-2a8d-4f3e-9d51-7c0a4e2b9f10",
  "object": "call",
  "assistant_id": "0e6f4c8a-3b2d-4a1e-8f7c-5d9b2a1c3e4f",
  "phone_number_id": "4d3c2b1a-0f9e-4d8c-b7a6-958473625140",
  "contact_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
  "direction": "outbound",
  "status": "completed",
  "from": "+48732127980",
  "to": "+48601234567",
  "started_at": "2026-10-01T09:14:03Z",
  "ended_at": "2026-10-01T09:17:41Z",
  "duration_sec": 218,
  "summary": "Jan added the company name and tax ID; asked for a call tomorrow at 10:00 about payments.",
  "outcome": "message",
  "sentiment": "positive",
  "tags": [],
  "has_recording": true,
  "end_reason": "agent_hangup",
  "callback_id": "0f1e2d3c-4b5a-4968-8776-655443322110",
  "external_id": "gapli-evt-1842",
  "outreach": {
    "sequence_id": "11111111-1111-4111-8111-111111111111",
    "enrollment_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
    "external_id": "gapli-evt-1842",
    "step": 1
  },
  "caller_spoke": true,
  "caller_words": 112
}

Kalendarz, CRM i automatyzacje

Gotowe integracje, bez pisania kodu

  • Kalendarz Google

    Asystent sprawdza prawdziwą dostępność oraz umawia, przekłada i odwołuje wizyty w Twoim kalendarzu. Czyta tylko zajęte terminy, nigdy tytuły wydarzeń.

  • HubSpot

    Każda rozmowa trafia do kontaktu, rezerwacje stają się spotkaniami, a prośby o oddzwonienie zadaniami. Opcjonalnie tworzy kontakty i deale.

  • Pipedrive

    Rozmowy jako aktywności lub notatki, rezerwacje jako spotkania, oddzwonienia jako aktywności. Opcjonalnie tworzy osoby, deale lub leady.

  • Zapier, Make i n8n

    Instrukcje krok po kroku w panelu. Działają na webhookach i API, które są w każdym pakiecie.

Pakiety i limity

Jakiego pakietu potrzebujesz

Limity na konto: zapytania na minutę (także na klucz, okno przesuwne) i na dobę (UTC). Po przekroczeniu limitu dostaniesz 429 rate_limited z nagłówkiem Retry-After.

PakietZapytania / minZapytania / dobaKlucze APIWebhooki
Okres próbny (dostęp testowy)20100011
Line30200022
Desk6010 00055
Office18050 0001010
Network600200 0002520

Widżet głosowy na stronę: od pakietu Office.

Potrzebujesz wyższych limitów? Napisz do nas — możemy je zwiększyć dla Twojego konta.

Wczesny dostęp: dołącz do pierwszych firm, w których telefony odbiera AI.

Zaczynamy integrację?

Rozpocznij okres próbny, utwórz testowy klucz API i wyślij pierwsze zapytanie w kilka minut. API jest w każdym pakiecie, od Line.