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ę.
https://telofia.com/api/v1curl https://telofia.com/api/v1/me \
-H "Authorization: Bearer tf_live_…"{
"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
}
}Co możesz połączyć
Siedem sposobów, by włączyć Telofia w Twoją firmę
Wybierz, co pasuje: automatyzacje bez kodu, kilka linijek kodu albo pełna integracja w obie strony.
- W każdym pakiecie
REST API
Pobieraj rozmowy z transkrypcjami i nagraniami, wizyty, kontakty i zadania. Twórz i zmieniaj kontakty, odwołuj i przekładaj wizyty oraz zamykaj zadania z własnego systemu.
- W każdym pakiecie
Webhooki
Podpisane zdarzenia JSON w czasie rzeczywistym: rozmowa rozpoczęta lub zakończona, wizyta umówiona, odwołana lub przełożona, nowy kontakt, prośba o oddzwonienie.
- W każdym pakiecie
Dane na żywo przez MCP
Podłącz swój serwer MCP, a asystent w trakcie rozmowy sprawdzi stan magazynu, dostępność albo status zamówienia.
- Od pakietu Office
Widżet głosowy na stronę
Jeden znacznik script dodaje przycisk „Porozmawiaj z nami”. Odwiedzający rozmawiają z asystentem w przeglądarce.
- W każdym pakiecie
Onboarding telefoniczny
Przekaż nowe rejestracje ze swojego systemu, a asystent zadzwoni do tych osób i pomoże im zacząć.
- W każdym pakiecie
Kalendarz i CRM
Kalendarz Google do rezerwacji, HubSpot i Pipedrive do kontaktów, rozmów, spotkań i zadań.
- W każdym pakiecie
Zapier, Make, n8n
Automatyzacje bez kodu oparte na webhookach i API: Arkusze Google, Slack, e-mail, dowolny CRM.
Szybki start
Pierwsze zapytanie w dwie minuty
- 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
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
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 https://telofia.com/api/v1/me \
-H "Authorization: Bearer tf_live_…"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 kluczPobierz bieżące konto
Zwraca konto, do którego należy klucz API. Przydatne jako wywołanie „test połączenia”.
- GET
/assistantsUprawnienie: każdy kluczLista 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 kluczPobierz 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:assistantsZmień 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 kluczLista numerów telefonów
Numery telefonów konta z krajem, rodzajem, stanem i asystentem, który je odbiera.
- GET
/callsUprawnienie: read:callsLista rozmów
Rozmowy od najnowszych, bez transkrypcji. Rozmowy demo nigdy nie są uwzględniane.
- GET
/calls/{id}Uprawnienie: read:callsPobierz rozmowę
Pojedyncza rozmowa z pełną transkrypcją i wizytami umówionymi w jej trakcie.
- GET
/calls/{id}/recordingUprawnienie: read:callsPobierz 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:appointmentsLista wizyt
Nadchodzące wizyty w kolejności rozpoczęcia (domyślnie od chwili obecnej).
- GET
/appointments/{id}Uprawnienie: read:appointmentsPobierz wizytę
Jedna wizyta Twojego konta.
- POST
/appointments/{id}/cancelUprawnienie: write:appointmentsOdwoł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:appointmentsPrzełóż 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:contactsLista kontaktów
Osoby, które do Ciebie dzwoniły, od najnowszych. Asystent pamięta je między rozmowami.
- GET
/contacts/{id}Uprawnienie: read:contactsPobierz kontakt
Jeden kontakt z imieniem, e-mailem, tagami, notatkami i źródłem imienia oraz e-maila (call, system lub manual).
- POST
/contactsUprawnienie: write:contactsUtwó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:contactsZmień 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:tasksLista zadań
Prośby o oddzwonienie, wiadomości i inne zadania z rozmów, od najnowszych.
- GET
/tasks/{id}Uprawnienie: read:tasksPobierz zadanie
Jedno zadanie Twojego konta.
- PATCH
/tasks/{id}Uprawnienie: write:tasksZmień zadanie
Ustawia status zadania: open, done lub dismissed, np. gdy Twój zespół oddzwonił już do klienta.
- POST
/calls/outboundUprawnienie: write:callsUtwó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:callsPobierz 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:callsAnuluj 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:messagesLista 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:messagesWyś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 transkrypcjiread:appointmentsOdczyt wizytwrite:appointmentsOdwoływanie i przekładanie wizytread:contactsOdczyt kontaktówwrite:contactsTworzenie i edycja kontaktówread:tasksOdczyt zadań i próśb o oddzwonieniewrite:tasksZmiana statusu zadańwrite:callsUruchamianie połączeń wychodzących, sprawdzanie i anulowanie ichread:messagesOdczyt SMS-ów i odpowiedzi klientówwrite:messagesWysyłanie SMS-ów do Twoich kontaktówread: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żniony403insufficient_scope — klucz nie ma uprawnienia endpointu; plan_required — konto nie ma aktywnego pakietu albo nagranie jest starsze niż historia nagrań w pakiecie404not_found — obiekt nie istnieje na Twoim koncie409conflict — 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-After500server_error — wystąpił błąd po naszej stronie503service_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ńczonaPodsumowanie, 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ętaOdebrano 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 / zadanieDzwoniący poprosił o oddzwonienie lub zostawił zadanie.
contact.createdNowy kontaktOsoba dzwoniąca po raz pierwszy została zapisana jako kontakt.
outbound_call.finishedPołączenie wychodzące zakończoneZaplanowane 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.bookedTelofia-DeliveryUnikalny identyfikator dostawyUser-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.
{
"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.
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);
}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", ""))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.
<script src="https://telofia.com/widget.js" data-key="tfw_…" async></script>// 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 pageMasz 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.
// 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
- Numer telefonu, najlepiej w formacie E.164. Numery krajowe odczytujemy z kierunkowym kraju Twojego konta.
phonestringwymagany - Musi mieć wartość true: osoba zgodziła się na kontakt telefoniczny, np. w Twoim formularzu rejestracji.
consenttruewymagany - Imię i nazwisko, do 120 znaków.
namestringopcjonalny - Adres e-mail.
emailstringopcjonalny - Co asystent powinien wiedzieć o tej osobie, do 1000 znaków (np. wybrany pakiet).
contextstringopcjonalny - Identyfikator osoby w Twoim systemie (tekst lub liczba). Ten sam identyfikator zapisujemy tylko raz.
external_idstring | numberopcjonalny
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}'{
"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.
- Dokładnie jedno z: external_id (jak przy zapisie), enrollment_id (z odpowiedzi na zapis) albo phone.
external_id | enrollment_id | phonewymagany - Opcjonalny powód, do 200 znaków, zapisywany w dzienniku zdarzeń Twojego konta.
reasonopcjonalny
- 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 -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"}'{
"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.
{
"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.
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.