Aller au contenu

Pour les développeurs

Connectez Telofia aux outils que vous utilisez déjà

Récupérez appels, transcriptions, rendez-vous et contacts via une API REST, recevez des webhooks signés dès que quelque chose se produit, laissez l’assistant consulter vos données en direct pendant l’appel et ajoutez un assistant vocal à votre site en une ligne de code.

L’API REST et les webhooks sont inclus dans tous les forfaits, dès Line ; les forfaits supérieurs ont des limites plus élevées. Pendant l’essai gratuit, vous avez un accès de test : 1 clé API, 20 requêtes par minute.

URL de base de l’API
https://telofia.com/api/v1
curl · Tester la clé
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
  }
}

Démarrage rapide

Votre première requête en deux minutes

  1. 1

    Créez une clé API

    Dans le tableau de bord, ouvrez Développeurs → Clés API, nommez la clé et choisissez ses autorisations. La clé (tf_live_…) n’est affichée qu’une fois : enregistrez-la tout de suite dans votre gestionnaire de secrets.

  2. 2

    Testez la connexion

    Appelez GET /me. Cela fonctionne avec toute clé valide et renvoie votre compte, les autorisations de la clé et les limites de votre forfait.

  3. 3

    Lisez les données ou abonnez-vous aux événements

    Listez appels, rendez-vous et contacts, ou ajoutez un point de réception dans Développeurs → Webhooks pour recevoir les événements en direct.

curl · Tester la clé
curl https://telofia.com/api/v1/me \
  -H "Authorization: Bearer tf_live_…"
JavaScript : tous les appels avec réservation, page par page
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;
}

API REST v1

Appels, rendez-vous et contacts via HTTPS

Du JSON en entrée comme en sortie. Chaque requête est authentifiée par une clé API et limitée selon votre forfait. Les requêtes apparaissent dans le tableau de bord (méthode, chemin, statut et durée — jamais le contenu des requêtes ni des réponses).

Authentification

Créez une clé dans Clés API et envoyez-la comme jeton Bearer. Les clés commencent par tf_live_ et sont liées à un seul compte.

Authorization: Bearer tf_live_…

Pagination

Les endpoints de liste renvoient jusqu’à limit éléments (1–100, 25 par défaut) dans un ordre stable. Si has_more vaut true, passez next_cursor tel quel comme cursor (avec les mêmes filtres) pour obtenir la page suivante. Un curseur invalide renvoie 400 invalid_request.

Erreurs

Les erreurs utilisent les codes de statut HTTP standard et un corps JSON contenant un type et un message lisible.

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

Points de terminaison

  • GET/meAutorisation: toute clé

    Récupérer le compte actuel

    Renvoie le compte auquel appartient la clé API. Pratique pour « tester la connexion ».

  • GET/assistantsAutorisation: toute clé

    Lister les assistants

    Vos assistants avec leur langue, leur statut et les numéros de téléphone attribués. Utilisez l’id comme assistant_id pour filtrer les appels.

  • GET/assistants/{id}Autorisation: toute clé

    Récupérer un assistant

    Un assistant avec sa langue, son statut et ses numéros. Avec le scope read:assistants (ou write:assistants), renvoie aussi le prompt : instructions, greeting, company_profile et updated_at.

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

    Modifier le prompt de l’assistant

    Modifie les instructions, l’accueil ou le profil de l’entreprise. Les champs omis restent inchangés. Mêmes limites que dans le tableau de bord : instructions jusqu’à 20 000 caractères, profil jusqu’à 10 000, accueil de 5 à 600 caractères qui doit indiquer que l’appelant parle à une IA (sinon 400 avec code ai_disclosure). Chaque modification est enregistrée dans l’historique des versions (source API) et s’applique dès le prochain appel. Les règles d’honnêteté de la plateforme (mention de l’IA et de l’enregistrement) s’appliquent toujours.

  • GET/phone-numbersAutorisation: toute clé

    Lister les numéros de téléphone

    Les numéros de téléphone de votre compte avec pays, type, statut et l’assistant qui y répond.

  • GET/callsAutorisation: read:calls

    Lister les appels

    Les appels, du plus récent au plus ancien, sans transcription. Les appels de démo ne sont jamais inclus.

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

    Récupérer un appel

    Un appel avec sa transcription complète et les rendez-vous pris pendant l’appel.

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

    Obtenir l’enregistrement d’un appel

    Liens signés (valables 1 heure) pour écouter et télécharger l’enregistrement MP3 (stéréo : appelant à gauche, assistant à droite). Un enregistrement plus ancien que l’historique de votre forfait renvoie 403 plan_required avec recording_history_days ; un appel sans enregistrement renvoie 404.

  • GET/appointmentsAutorisation: read:appointments

    Lister les rendez-vous

    Les rendez-vous à venir, par ordre chronologique (à partir de maintenant par défaut).

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

    Récupérer un rendez-vous

    Un rendez-vous de votre compte.

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

    Annuler un rendez-vous

    Annule un rendez-vous à venir, le retire du calendrier Google connecté et envoie le webhook appointment.canceled. calendar_sync indique si le calendrier a été mis à jour.

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

    Déplacer un rendez-vous

    Déplace un rendez-vous à venir vers un nouvel horaire. Répond 409 si le créneau chevauche un autre rendez-vous ou une période occupée du calendrier connecté. Sans ends_at, la durée reste la même. Envoie le webhook appointment.rescheduled.

  • GET/contactsAutorisation: read:contacts

    Lister les contacts

    Vos appelants, du plus récent au plus ancien. L’assistant s’en souvient d’un appel à l’autre.

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

    Récupérer un contact

    Un contact avec nom, e-mail, tags, notes et l’origine du nom et de l’e-mail (call, system ou manual).

  • POST/contactsAutorisation: write:contacts

    Créer un contact

    Ajoute un contact, par exemple depuis votre CRM, pour que l’assistant connaisse le nom de l’appelant. Le nom et l’e-mail enregistrés via l’API sont marqués manual et l’assistant ne les écrase jamais. Renvoie 409 avec contact_id si le numéro existe déjà. Envoie le webhook contact.created.

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

    Modifier un contact

    Modifie le nom, l’e-mail, les notes ou le blocage. Les champs omis restent inchangés et null vide un champ. Un nom ou un e-mail modifié est marqué manual, l’assistant ne l’écrasera donc pas. Le numéro de téléphone ne peut pas être modifié.

  • GET/tasksAutorisation: read:tasks

    Lister les tâches

    Demandes de rappel, messages et autres tâches issues des appels, des plus récentes aux plus anciennes.

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

    Récupérer une tâche

    Une tâche de votre compte.

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

    Modifier une tâche

    Passe le statut de la tâche à open, done ou dismissed, par exemple une fois que votre équipe a rappelé le client.

  • POST/calls/outboundAutorisation: write:calls

    Créer un appel sortant

    Votre assistant appelle une personne depuis le numéro de votre entreprise avec l’objectif que vous indiquez, comme une étape d’une séquence d’onboarding. L’appel part maintenant ou à call_at, toujours pendant les horaires d’appel (lun–sam, 9 h–20 h dans le fuseau horaire de votre compte ; en dehors, il est reporté au prochain créneau autorisé et call_at indique quand). Sans réponse, il réessaie jusqu’à 3 fois, à 2 heures d’intervalle. Renvoie 202 avec l’appel au statut scheduled. Erreurs : 400 consent_missing, invalid_phone, number_not_callable, international ou invalid_call_at ; 409 opted_out (la personne ne souhaite pas être appelée), already_scheduled (avec existing_id), assistant_not_live, over_quota ou no_outbound_number ; 429 daily_outbound_limit avec limit (200 appels par 24 heures par défaut). Le résultat arrive dans les webhooks call.completed et outbound_call.finished avec votre external_id.

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

    Récupérer un appel sortant

    Statut d’un appel sortant planifié : scheduled, dialing, done (avec le call_id de la conversation), failed (last_error, p. ex. no_answer après toutes les tentatives) ou canceled. Fonctionne aussi avec le callback_id des webhooks pour les appels d’onboarding et les rappels.

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

    Annuler un appel sortant

    Annule un appel sortant créé via l’API qui est encore planifié. Peut être répété sans risque (un appel déjà annulé renvoie 200). Renvoie 409 not_cancelable si l’appel est en cours de numérotation ou terminé, ou pour les appels d’onboarding (retirez plutôt la personne avec POST /api/outreach/unenroll).

  • GET/messagesAutorisation: read:messages

    Lister les SMS

    SMS envoyés par votre compte et réponses des clients, du plus récent au plus ancien. phone est le numéro du client (destinataire ou expéditeur d’une réponse). generated_by : template, ai ou manual ; charged_from : plan ou credits.

  • POST/messagesAutorisation: write:messages

    Envoyer un SMS

    Envoie un SMS à l’un de vos contacts ou appelants : votre propre texte, ou un modèle de votre compte avec vars (date, time, name, service, link, amount). Compte dans le forfait SMS de votre offre et les SMS supplémentaires. Erreurs : 400 invalid_request (consent_missing, invalid_phone, too_long, missing_vars) ; 409 conflict (opted_out — le client a répondu STOP, over_quota, recipient_not_allowed — créez d’abord le contact, blocked_destination) ; 429 rate_limited.

Autorisations

Chaque clé a les portées choisies lors de sa création. Les portées ne peuvent pas être modifiées ensuite : créez une nouvelle clé si vous avez besoin de plus. Si la clé n’a pas la portée requise, les endpoints répondent 403 insufficient_scope. GET /me, /assistants et /phone-numbers fonctionnent avec toute clé valide.

  • read:callsLire les appels, résumés et transcriptions
  • read:appointmentsLire les rendez-vous
  • write:appointmentsAnnuler et déplacer des rendez-vous
  • read:contactsLire les contacts
  • write:contactsCréer et modifier des contacts
  • read:tasksLire les tâches et demandes de rappel
  • write:tasksModifier le statut des tâches
  • write:callsLancer des appels sortants, les consulter et les annuler
  • read:messagesLire les SMS et les réponses des clients
  • write:messagesEnvoyer des SMS à vos contacts
  • read:assistantsLire le prompt de l’assistant (instructions, accueil, profil de l’entreprise)
  • write:assistantsModifier le prompt de l’assistant

Erreurs

  • 400invalid_request — un paramètre est manquant ou invalide
  • 401unauthorized — clé API manquante, invalide ou révoquée
  • 403insufficient_scope — la clé n’a pas la portée requise par l’endpoint ; plan_required — le compte n’a pas de forfait actif ou l’enregistrement est plus ancien que l’historique d’enregistrements du forfait
  • 404not_found — l’objet n’existe pas dans votre compte
  • 409conflict — le créneau est déjà pris, le rendez-vous ne peut pas être modifié, un contact avec ce numéro existe déjà, ou un appel sortant ne peut pas être passé (code : opted_out, already_scheduled, assistant_not_live, over_quota, no_outbound_number, not_cancelable)
  • 429rate_limited — trop de requêtes ; réessayez après le délai indiqué par l’en-tête Retry-After
  • 500server_error — une erreur s’est produite de notre côté
  • 503service_unavailable — problème temporaire ; réessayez après le délai indiqué dans l’en-tête Retry-After

La référence complète de l’API, avec paramètres et exemples de réponses, se trouve dans le tableau de bord : Développeurs → Référence de l’API.

Webhooks

Des événements envoyés à votre serveur en temps réel

Ajoutez une adresse https dans le tableau de bord, choisissez les événements, et nous envoyons une enveloppe JSON signée (POST) dès que quelque chose se produit. « Envoyer un test » livre un exemple de votre premier événement, pratique pour mapper les champs dans Zapier ou Make.

Événements

  • call.completedAppel terminé

    Résumé, résultat, ressenti, transcription, contact et rendez-vous ; pour les appels sortants, également la séquence ou la requête API (callback_id, external_id, outreach).

  • call.startedAppel commencé

    Un appel a été décroché (entrant, sortant ou web).

  • appointment.bookedRendez-vous pris

    L’assistant a pris un rendez-vous.

  • appointment.canceledRendez-vous annulé

    Un rendez-vous a été annulé par téléphone ou via l’API.

  • appointment.rescheduledRendez-vous déplacé

    Un rendez-vous a été déplacé à un nouvel horaire (avec l’horaire précédent).

  • task.createdRappel / tâche créé

    Un appelant a demandé à être rappelé ou a laissé une tâche.

  • contact.createdNouveau contact

    Un nouvel appelant a été enregistré comme contact.

  • outbound_call.finishedAppel sortant terminé

    Un appel sortant planifié (API, séquence d’onboarding ou rappel) est terminé ou a échoué — y compris quand personne n’a répondu après toutes les tentatives.

En-têtes de chaque livraison

  • Telofia-SignatureSignature : t=<temps unix>,v1=<HMAC-SHA256 hex>
  • Telofia-EventType d’événement, p. ex. appointment.booked
  • Telofia-DeliveryIdentifiant unique de la livraison
  • User-AgentTelofia-Webhooks/1.0 · Identifie notre expéditeur de webhooks

Livraison et nouvelles tentatives

  • Répondez avec n’importe quel statut 2xx en moins de 10 secondes. Les redirections ne sont pas suivies.
  • Les livraisons échouées sont relancées après 1 min, 5 min, 30 min, 2 h, 6 h et 12 h (7 tentatives, environ 21 heures).
  • Un même événement peut arriver plusieurs fois. Utilisez l’id de l’événement pour ignorer les doublons.
  • Seules les adresses https publiques sont acceptées. Après 50 échecs consécutifs, le point de réception est mis en pause et le tableau de bord en indique la raison.
  • Le tableau de bord conserve un journal des livraisons avec codes de statut et réponses, et permet de relancer une livraison à la main.
Exemple de livraison : 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"
      }
    ]
  }
}

Vérifier les signatures. Chaque envoi comporte un en-tête Telofia-Signature au format t=timestamp,v1=signature. Calculez le HMAC-SHA256 de « timestamp.raw_body » avec votre secret de signature et comparez-le à v1. Rejetez les horodatages de plus de 5 minutes.

Node.js · Vérifier la signature
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 · Vérifier la signature
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", ""))
Exemple de récepteur (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);

Valeurs stables

Ces valeurs dans les appels (API et webhooks call.*) et dans les appels sortants (outbound_call.finished, /calls/outbound) constituent un contrat stable : nous pouvons ajouter de nouvelles valeurs, mais nous ne modifierons ni ne supprimerons celles-ci. Les tentatives sortantes sans réponse, occupées ou rejetées ne créent jamais d’appel, il n’y a donc pas de call.completed pour elles : vous recevez outbound_call.finished avec last_error no_answer après la dernière tentative. Un appel pris par une messagerie vocale compte comme décroché (généralement un appel court avec outcome other).

Statut de l’appel

in_progress
L’appel est en cours.
completed
La personne a parlé avec l’assistant.
missed
Personne n’a parlé, ou l’assistant n’a pas pu prendre l’appel (voir end_reason).
failed
L’appel n’a pas pu être traité à cause d’une erreur.

Résultat de l’appel

booked
Un rendez-vous a été pris.
transferred
L’appel a été transféré à une personne.
message
Un message, une demande de rappel, une commande ou un suivi a été enregistré.
info
La personne a obtenu des informations ; rien d’autre n’était nécessaire.
spam
Spam, démarchage ou numéro bloqué.
other
Tout autre cas, p. ex. un appel très court ou une messagerie vocale.

Motif de fin d’appel (end_reason)

caller_hangup
L’autre personne a raccroché.
caller_hangup_greeting
Appel sortant : la personne a raccroché pendant l’accueil ou juste après, sans dire un mot (appel de 45 s maximum). caller_spoke vaut alors false.
agent_hangup
L’assistant a mis fin à l’appel après avoir pris congé.
transferred
L’appel a été transféré à une personne.
silence
Terminé après un long silence.
max_duration
La durée maximale d’appel a été atteinte.
spam
Terminé comme spam.
blocked
Le numéro est bloqué dans les contacts.
busy
Toutes les lignes du compte étaient occupées.
over_quota
Plus de minutes d’appel.
trial_expired
L’essai gratuit est terminé.
inactive
Aucun forfait actif.
assistant_not_live
L’assistant est en pause.
no_assistant
Aucun assistant ne répond sur ce numéro.
ai_budget
Traité sans IA en raison d’une limite temporaire ; l’équipe a été invitée à rappeler.
error
Une erreur technique a mis fin à l’appel.

Statut de l’appel sortant

scheduled
En attente de son horaire (ou de la prochaine tentative).
dialing
Appel en cours de numérotation.
done
Décroché ; call_id correspond à la conversation.
failed
Sans réponse après toutes les tentatives, ou impossible à passer (voir last_error).
canceled
Annulé (API, personne retirée de la séquence ou séquence désactivée).

last_error de l’appel sortant

no_answer
Personne n’a répondu (y compris ligne occupée ou appel rejeté).
failed
L’assistant n’était pas disponible au moment de l’appel (forfait, minutes ou pause).
no_result
Aucun résultat de l’appel dans les 15 minutes.
expired
L’appel avait plus de 2 heures de retard, il n’a donc pas été passé.
outside_window
Reporté aux prochains horaires d’appel (le statut reste scheduled).
dial_error
Le réseau téléphonique a refusé l’appel (envoyé sous la forme dial_<reason>).
over_quota
Plus de minutes d’appel.
inactive
Aucun forfait actif.
assistant_not_live
L’assistant est en pause.
no_outbound_number
Aucun numéro d’entreprise pour appeler.
canceled_by_api
Annulé avec DELETE /calls/outbound/{id}.
unenrolled
La personne a été retirée de la séquence (POST /api/outreach/unenroll).
opt_out
La personne ne souhaite pas être appelée.
replaced
Remplacé par un rappel plus récent vers le même numéro.

Données en direct via MCP

L’assistant consulte votre système pendant l’appel

Connectez un serveur MCP distant (Model Context Protocol) : catalogue de votre boutique, stock, système de réservation ou de commandes. Vous choisissez précisément les outils et ressources (en lecture seule) que l’assistant peut utiliser.

Apprendre à l’avance

Pour les contenus qui changent peu : tarifs, descriptions de produits, conditions. Les ressources et outils en lecture seule choisis sont importés dans les connaissances de l’assistant toutes les 1 à 168 heures ou à la demande. Aucun délai pendant l’appel.

Vérifier en direct pendant l’appel

Pour ce qui change : stock, disponibilités, statut de commande. L’assistant appelle l’outil pendant la conversation avec un bref « un instant, je vérifie ». Si votre serveur ne répond pas en 2,5 secondes environ, il indique ne pas pouvoir confirmer pour l’instant et propose un rappel de votre équipe.

Ce qu’il vous faut

  • Un serveur MCP accessible en https (Streamable HTTP, p. ex. https://mcp.example.com/mcp ; les anciens serveurs HTTP+SSE sont détectés automatiquement).
  • Authentification : aucune, un jeton Bearer ou un en-tête personnalisé comme X-API-Key. Les secrets sont chiffrés (AES-256-GCM) et jamais envoyés au navigateur.
  • Des outils en lecture seule. Les outils marqués comme destructifs sont bloqués ; pour ceux qui ne sont pas marqués en lecture seule, vous confirmez qu’ils ne font que lire des données.
  • Jusqu’à 5 sources et 50 outils en direct par assistant. Les vérifications en direct doivent répondre en 2 secondes environ.
  • En option pour le widget du site : sur un outil « Au début de l’appel », choisissez l’argument qui reçoit le jeton d’identité du visiteur connecté (voir Widget vocal pour votre site).

Les arguments sont validés selon le schéma de l’outil, les résultats sont raccourcis et traités strictement comme des données (jamais comme des instructions), et chaque vérification est journalisée sans données de l’appelant. Les adresses privées et internes sont refusées.

Widget vocal pour votre site

Votre assistant sur votre site, en une ligne de code

Les visiteurs cliquent sur un bouton et parlent, dans le navigateur, avec le même assistant qui répond à votre téléphone. Les appels web utilisent les minutes de votre forfait, sans frais de téléphonie.

Dès Office

  • Environ 14 Ko, sans dépendances, isolé dans un Shadow DOM : les styles de votre site ne peuvent pas le casser.
  • Parle la langue de votre page (<html lang>) ou celle définie avec data-lang="de".
  • Sous-titres en direct ; si le micro est bloqué, le visiteur peut écrire et l’assistant répond à voix haute.
  • Limitez le widget à vos domaines : il ne démarrera que sur ces sites.
Code d’intégration (votre clé est dans le tableau de bord)
<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

Content Security Policy stricte ? Autorisez script-src https://telofia.com https://cdn.jsdelivr.net, connect-src https://telofia.com wss://*.livekit.cloud https://*.livekit.cloud et media-src blob:.

Utilisateurs connectés (jeton d’identité)

Quand vos visiteurs sont connectés sur votre site, l’assistant peut savoir qui parle sans demander son e-mail.

  • Votre serveur émet un jeton signé et de courte durée pour l’utilisateur connecté (p. ex. HMAC, valable 15 minutes). Ne placez jamais d’adresse e-mail ni d’identifiant utilisateur dans le navigateur : n’importe qui pourrait saisir celui d’un autre dans la console.
  • Appelez TelofiaWidget.identify({ token }) à tout moment ; un appel ultérieur remplace le jeton et identify(null) l’efface. Avant le chargement de widget.js, définissez window.TelofiaIdentity = { token, expires_at } : il est lu au début de la conversation et un jeton expiré est ignoré.
  • Dans le tableau de bord, choisissez l’argument du jeton d’identité de votre outil MCP « Au début de l’appel ». Telofia transmet le jeton tel quel uniquement à cet argument, ne le stocke ni ne l’affiche jamais et le tient à l’écart des transcriptions, résumés, webhooks et journaux ; l’assistant ne le voit jamais.
  • De 8 à 400 caractères : lettres, chiffres et . _ ~ + / = - (p. ex. base64url). Renouvelez-le avant son expiration, par exemple toutes les 10 minutes. Votre serveur MCP le vérifie ; un jeton invalide ou expiré doit donner le même résultat qu’aucun jeton. Les appels téléphoniques et les conversations sans jeton fonctionnent comme avant.
Jeton d’identité
// 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 téléphonique

Vos nouveaux clients reçoivent automatiquement un appel de bienvenue

Quand quelqu’un s’inscrit dans votre système, ajoutez-le à une séquence d’appels. L’assistant l’appelle depuis le numéro de votre entreprise, l’aide à démarrer et reprend contact quelques jours plus tard. Les appels ont lieu pendant les horaires d’appel de la séquence : par défaut du lundi au samedi, de 9 h à 20 h dans votre fuseau horaire, ou aux jours et heures que vous avez définis.

POST/api/outreach/enroll

Authentifiez-vous avec la clé de la séquence (tlo_…) du tableau de bord, en jeton Bearer ou dans l’en-tête X-Telofia-Key. Chaque séquence a sa propre clé.

Corps de la requête

  • phonestringobligatoire
    Numéro de téléphone, idéalement au format E.164. Les numéros nationaux sont lus avec l’indicatif du pays de votre compte.
  • consenttrueobligatoire
    Doit valoir true : la personne a accepté d’être contactée par téléphone, p. ex. dans votre formulaire d’inscription.
  • namestringfacultatif
    Nom, 120 caractères maximum.
  • emailstringfacultatif
    Adresse e-mail.
  • contextstringfacultatif
    Ce que l’assistant doit savoir sur cette personne, 1 000 caractères maximum (p. ex. le forfait choisi).
  • external_idstring | numberfacultatif
    L’identifiant de la personne dans votre système (texte ou nombre). Un même identifiant n’est inscrit qu’une fois.
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
  }
}

Réponses

  • 201 : inscrite. enrollment_id, external_id, status, next_call_at (heure du premier appel), intro_sms_at (heure d’envoi du SMS avant le premier appel, ou null) et limits { per_day, remaining_today }
  • 200 : déjà inscrite (même external_id, ou ce numéro est déjà en cours dans la séquence), avec duplicate: true
  • 400 : invalid_request, invalid_phone ou consent_required
  • 401 : invalid_key
  • 409 : sequence_disabled, consent_missing ou opted_out (la personne ne souhaite pas être appelée)
  • 429 : limit, la limite quotidienne d’inscriptions de la séquence est atteinte (500 nouvelles personnes par 24 heures ; le corps contient limit). rate_limited : plus de 120 requêtes par minute avec cette clé (en-tête Retry-After)

Horaires d’appel : dans le tableau de bord, définissez pour chaque séquence les jours et une plage horaire entre 7 h et 21 h. Sans réponse, l’assistant réessaie jusqu’à 3 fois, à 2 heures d’intervalle, pendant ces horaires.

SMS avant l’appel : s’il est activé pour la séquence (tableau de bord), la personne reçoit un court message depuis le numéro qui servira à l’appeler, par défaut 20 minutes avant le premier appel (5–120 min). Si le premier appel devait avoir lieu plus tôt, il est décalé pour que le SMS parte toujours en premier, pendant les horaires d’appel. Un SMS en échec (la personne a répondu STOP, le quota SMS est épuisé) n’empêche jamais l’appel. Chaque SMS est décompté de votre quota SMS et apparaît dans le tableau de bord, rubrique SMS.

Accueil : dans le tableau de bord, vous définissez la première phrase de l’appel pour chaque étape (ou par défaut pour toute la séquence) — un texte fixe avec les variables {first_name}, {first_name_vocative} (vocatif polonais, p. ex. Krystianie), {assistant_name}, Telofia (nom de l’entreprise dans les appels, réglages de l’assistant) et {name}, avec une variante pour les inscriptions sans nom — ou un mode où l’assistant rédige la première phrase à partir de name, context, de l’objectif de l’étape et du résultat des outils « Au début de l’appel ». Le fait qu’un assistant IA appelle et que l’appel est enregistré est toujours ajouté à la première phrase si le texte ne le dit pas.

Retirer une personne de la séquence

POST/api/outreach/unenroll

Quand quelqu’un n’a plus besoin des appels (p. ex. après sa première vente, ou s’il a résilié), arrêtez sa séquence avec la même clé. Les appels planifiés sont annulés immédiatement. Un appel déjà en cours n’est pas interrompu, mais aucun autre appel ne suit.

  • external_id | enrollment_id | phoneobligatoire
    Exactement un parmi : external_id (tel qu’envoyé lors de l’inscription), enrollment_id (issu de la réponse d’inscription) ou phone.
  • reasonfacultatif
    Motif facultatif, 200 caractères maximum, conservé dans le journal d’audit de votre compte.
  • 200 : arrêtée (changed: true) ou déjà terminée : status stopped, completed ou failed (changed: false). Peut être répété sans risque.
  • 400 : invalid_request (aucun identifiant ou plusieurs) ou invalid_phone
  • 401 : invalid_key
  • 404 : not_found, aucune inscription correspondante dans cette séquence
  • 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
}

Résultats des appels dans les webhooks

call.started et call.completed pour les appels d’une séquence contiennent callback_id, external_id et outreach { sequence_id, enrollment_id, external_id, step } (tous null pour les appels entrants). Les tentatives sans réponse ne créent pas d’appel, il n’y a donc pas de call.completed pour elles : après la dernière tentative, vous recevez outbound_call.finished avec status failed et last_error no_answer. Les nouvelles tentatives d’une même étape comptent pour un seul résultat. call.completed contient aussi caller_spoke et caller_words (nombre de mots prononcés par la personne). Si quelqu’un a décroché puis raccroché pendant l’accueil ou juste après sans un mot, end_reason vaut caller_hangup_greeting.

call.completed pour un appel de séquence
{
  "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
}

Agenda, CRM et automatisation

Des intégrations prêtes à l’emploi, sans code

  • Google Agenda

    L’assistant vérifie les disponibilités réelles et prend, déplace et annule les rendez-vous dans votre agenda. Il ne lit que les créneaux occupés, jamais les titres des événements.

  • HubSpot

    Chaque appel est consigné sur le contact, les réservations deviennent des réunions et les rappels des tâches. Création de contacts et de transactions en option.

  • Pipedrive

    Appels en activités ou en notes, réservations en réunions, rappels en activités. Création de personnes, d’affaires ou de prospects en option.

  • Zapier, Make et n8n

    Guides pas à pas dans le tableau de bord. Ils reposent sur les webhooks et l’API, inclus dans tous les forfaits.

Forfaits et limites

Quel forfait choisir

Limites par compte : requêtes par minute (aussi par clé, fenêtre glissante) et par jour (UTC). Au-delà d’une limite, vous recevez 429 rate_limited avec un en-tête Retry-After.

OffreRequêtes / minRequêtes / jourClés APIWebhooks
Essai gratuit (accès de test)201 00011
Line302 00022
Desk6010 00055
Office18050 0001010
Network600200 0002520

Widget vocal pour votre site : à partir du forfait Office.

Besoin de limites plus élevées ? Contactez-nous, nous pouvons les augmenter pour votre compte.

Accès anticipé : faites partie des premières entreprises à confier leur téléphone à l’IA.

Prêt à connecter ?

Lancez l’essai gratuit, créez une clé API de test et envoyez votre première requête en quelques minutes. L’API est incluse dans tous les forfaits, dès Line.