Saltar al contenido

Para desarrolladores

Conecta Telofia con los sistemas que ya usas

Consulta llamadas, transcripciones, citas y contactos con una REST API, recibe webhooks firmados en cuanto algo ocurre, deja que el asistente consulte datos en vivo de tu sistema durante la llamada y añade un asistente de voz a tu web con una sola línea de código.

La REST API y los webhooks están incluidos en todos los planes, desde Line; los planes superiores tienen límites más altos. Durante la prueba gratuita tienes acceso de prueba: 1 clave de API, 20 peticiones por minuto.

URL base de la API
https://telofia.com/api/v1
curl · Probar la clave
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
  }
}

Inicio rápido

Tu primera petición en dos minutos

  1. 1

    Crea una clave de API

    En el panel ve a Desarrolladores → Claves de API, ponle nombre a la clave y elige sus permisos. La clave (tf_live_…) solo se muestra una vez, así que guárdala en tu gestor de secretos.

  2. 2

    Prueba la conexión

    Llama a GET /me. Funciona con cualquier clave válida y devuelve tu cuenta, los permisos de la clave y los límites de tu plan.

  3. 3

    Lee datos o suscríbete a eventos

    Lista llamadas, citas y contactos, o añade un endpoint en Desarrolladores → Webhooks para recibir eventos al momento.

curl · Probar la clave
curl https://telofia.com/api/v1/me \
  -H "Authorization: Bearer tf_live_…"
JavaScript: todas las llamadas con reserva, página a página
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

Llamadas, citas y contactos por HTTPS

JSON de entrada y de salida. Cada petición se autentica con una clave de API y tiene los límites de tu plan. Las peticiones aparecen en el panel (método, ruta, estado y tiempo; nunca el contenido de peticiones ni respuestas).

Autenticación

Crea una clave en Claves de API y envíala como token Bearer. Las claves empiezan por tf_live_ y pertenecen a una sola cuenta.

Authorization: Bearer tf_live_…

Paginación

Los endpoints de listas devuelven hasta limit elementos (1–100, 25 por defecto) en un orden estable. Si has_more es true, pasa next_cursor sin cambios como cursor (con los mismos filtros) para obtener la página siguiente. Un cursor no válido devuelve 400 invalid_request.

Errores

Los errores usan códigos de estado HTTP estándar y un cuerpo JSON con un tipo y un mensaje legible.

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

Endpoints

  • GET/mePermiso: cualquier clave

    Obtener la cuenta actual

    Devuelve la cuenta a la que pertenece la clave de API. Útil como llamada para “probar la conexión”.

  • GET/assistantsPermiso: cualquier clave

    Listar asistentes

    Tus asistentes con su idioma, estado y números de teléfono asignados. Usa el id como assistant_id para filtrar llamadas.

  • GET/assistants/{id}Permiso: cualquier clave

    Obtener un asistente

    Un asistente con idioma, estado y números de teléfono. Con el scope read:assistants (o write:assistants) devuelve también el prompt: instructions, greeting, company_profile y updated_at.

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

    Cambiar el prompt del asistente

    Cambia las instrucciones, el saludo o el perfil de la empresa. Los campos omitidos no cambian. Mismos límites que en el panel: instrucciones hasta 20.000 caracteres, perfil hasta 10.000, saludo de 5 a 600 caracteres y debe indicar que quien llama habla con una IA (si no, 400 con code ai_disclosure). Cada cambio se guarda en el historial de versiones del asistente (origen API) y se aplica desde la siguiente llamada. Las normas de honestidad de la plataforma (aviso de IA y de grabación) se aplican siempre.

  • GET/phone-numbersPermiso: cualquier clave

    Listar números de teléfono

    Los números de teléfono de tu cuenta con país, tipo, estado y el asistente que los atiende.

  • GET/callsPermiso: read:calls

    Listar llamadas

    Llamadas de la más reciente a la más antigua, sin transcripciones. Nunca incluye las llamadas de demostración.

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

    Obtener una llamada

    Una llamada con la transcripción completa y las citas reservadas durante ella.

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

    Obtener la grabación de una llamada

    Enlaces firmados (válidos 1 hora) para escuchar y descargar la grabación MP3 (estéreo: quien llama a la izquierda, el asistente a la derecha). Una grabación más antigua que el historial de grabaciones de tu plan devuelve 403 plan_required con recording_history_days; una llamada sin grabación devuelve 404.

  • GET/appointmentsPermiso: read:appointments

    Listar citas

    Próximas citas ordenadas por hora de inicio (desde ahora por defecto).

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

    Obtener una cita

    Una cita de tu cuenta.

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

    Cancelar una cita

    Cancela una cita próxima, la elimina del calendario de Google conectado y envía el webhook appointment.canceled. calendar_sync indica si se actualizó el calendario.

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

    Reprogramar una cita

    Mueve una cita próxima a una nueva hora. Responde 409 si la hora coincide con otra cita o con un tiempo ocupado en el calendario conectado. Sin ends_at la duración se mantiene. Envía el webhook appointment.rescheduled.

  • GET/contactsPermiso: read:contacts

    Listar contactos

    Las personas que te han llamado, de la más reciente a la más antigua. El asistente las recuerda de una llamada a otra.

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

    Obtener un contacto

    Un contacto con nombre, email, etiquetas, notas y el origen del nombre y del email (call, system o manual).

  • POST/contactsPermiso: write:contacts

    Crear un contacto

    Añade un contacto, por ejemplo desde tu CRM, para que el asistente conozca el nombre de quien llama. El nombre y el email guardados por la API se marcan como manual y el asistente nunca los sobrescribe. Devuelve 409 con contact_id si el número ya existe. Envía el webhook contact.created.

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

    Actualizar un contacto

    Cambia el nombre, el email, las notas o el bloqueo. Los campos omitidos no cambian y null vacía un campo. Un nombre o email modificado se marca como manual, así que el asistente no lo sobrescribirá. El número de teléfono no se puede cambiar.

  • GET/tasksPermiso: read:tasks

    Listar tareas

    Solicitudes de devolución de llamada, mensajes y otras tareas de las llamadas, de la más reciente a la más antigua.

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

    Obtener una tarea

    Una tarea de tu cuenta.

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

    Actualizar una tarea

    Cambia el estado de la tarea a open, done o dismissed, por ejemplo cuando tu equipo ya ha devuelto la llamada al cliente.

  • POST/calls/outboundPermiso: write:calls

    Crear una llamada saliente

    Tu asistente llama a una persona desde el número de tu empresa con el objetivo que indiques, como un paso de una secuencia de onboarding. La llamada se hace ahora o en call_at, siempre dentro del horario de llamadas (lun–sáb, 9:00–20:00 en la zona horaria de tu cuenta; fuera de él, pasa al siguiente momento permitido y call_at indica cuándo). Si nadie contesta, lo intenta hasta 3 veces, con 2 horas de diferencia. Devuelve 202 con la llamada en estado scheduled. Errores: 400 consent_missing, invalid_phone, number_not_callable, international o invalid_call_at; 409 opted_out (la persona pidió no recibir llamadas), already_scheduled (con existing_id), assistant_not_live, over_quota o no_outbound_number; 429 daily_outbound_limit con limit (200 llamadas cada 24 horas por defecto). El resultado llega en los webhooks call.completed y outbound_call.finished con tu external_id.

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

    Obtener una llamada saliente

    Estado de una llamada saliente programada: scheduled, dialing, done (con el call_id de la conversación), failed (last_error, p. ej. no_answer tras todos los intentos) o canceled. También funciona con el callback_id de los webhooks para las llamadas de onboarding y las devoluciones de llamada.

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

    Cancelar una llamada saliente

    Cancela una llamada saliente creada a través de la API que todavía está programada. Se puede repetir sin riesgo (si ya está cancelada, devuelve 200). Devuelve 409 not_cancelable si la llamada se está marcando o ya ha terminado, o si es una llamada de onboarding (en ese caso, saca a la persona con POST /api/outreach/unenroll).

  • GET/messagesPermiso: read:messages

    Listar SMS

    SMS enviados por tu cuenta y respuestas de clientes, los más recientes primero. phone es el número del cliente (destinatario o remitente de una respuesta). generated_by: template, ai o manual; charged_from: plan o credits.

  • POST/messagesPermiso: write:messages

    Enviar un SMS

    Envía un SMS a uno de tus contactos o llamantes: tu propio texto o una plantilla de tu cuenta con vars (date, time, name, service, link, amount). Cuenta para el límite de SMS de tu plan y los SMS adicionales. Errores: 400 invalid_request (consent_missing, invalid_phone, too_long, missing_vars); 409 conflict (opted_out — el cliente respondió STOP, over_quota, recipient_not_allowed — crea primero el contacto, blocked_destination); 429 rate_limited.

Permisos

Cada clave tiene los permisos elegidos al crearla. Los permisos no se pueden cambiar después: si necesitas más, crea una clave nueva. Si a la clave le falta el permiso necesario, los endpoints responden 403 insufficient_scope. GET /me, /assistants y /phone-numbers funcionan con cualquier clave válida.

  • read:callsLeer llamadas, resúmenes y transcripciones
  • read:appointmentsLeer citas
  • write:appointmentsCancelar y reprogramar citas
  • read:contactsLeer contactos
  • write:contactsCrear y editar contactos
  • read:tasksConsultar tareas y solicitudes de devolución de llamada
  • write:tasksCambiar el estado de las tareas
  • write:callsIniciar llamadas salientes, consultarlas y cancelarlas
  • read:messagesLeer SMS y respuestas de clientes
  • write:messagesEnviar SMS a tus contactos
  • read:assistantsLeer el prompt del asistente (instrucciones, saludo, perfil de la empresa)
  • write:assistantsCambiar el prompt del asistente

Errores

  • 400invalid_request: falta un parámetro o no es válido
  • 401unauthorized: clave de API ausente, no válida o revocada
  • 403insufficient_scope: a la clave le falta el permiso del endpoint; plan_required: la cuenta no tiene un plan activo o la grabación es más antigua que el historial de grabaciones del plan
  • 404not_found: el objeto no existe en tu cuenta
  • 409conflict — la hora ya está ocupada, la cita no se puede modificar, ya existe un contacto con este número de teléfono o no se puede hacer una llamada saliente (code: opted_out, already_scheduled, assistant_not_live, over_quota, no_outbound_number, not_cancelable)
  • 429rate_limited — demasiadas solicitudes; reintenta tras el tiempo de la cabecera Retry-After
  • 500server_error: algo salió mal por nuestra parte
  • 503service_unavailable: problema temporal; reintenta tras el tiempo indicado en la cabecera Retry-After

La referencia completa de la API, con parámetros y respuestas de ejemplo, está en el panel en Desarrolladores → Referencia de la API.

Webhooks

Eventos enviados a tu servidor en tiempo real

Añade un endpoint https en el panel, elige los eventos y enviaremos un sobre JSON firmado (POST) en cuanto ocurra algo. “Enviar prueba” entrega un ejemplo de tu primer evento, útil para mapear campos en Zapier o Make.

Eventos

  • call.completedLlamada completada

    Resumen, resultado, sentimiento, transcripción, contacto y citas; en las llamadas salientes, también la secuencia o la solicitud de la API (callback_id, external_id, outreach).

  • call.startedLlamada iniciada

    Se ha contestado una llamada (entrante, saliente o web).

  • appointment.bookedCita reservada

    El asistente ha reservado una cita.

  • appointment.canceledCita cancelada

    Se canceló una cita por teléfono o a través de la API.

  • appointment.rescheduledCita reprogramada

    Una cita se movió a una nueva hora (incluye la hora anterior).

  • task.createdDevolución de llamada / tarea creada

    Alguien pidió que le devolvieras la llamada o dejó una tarea.

  • contact.createdNuevo contacto

    Una persona que llamaba por primera vez se guardó como contacto.

  • outbound_call.finishedLlamada saliente finalizada

    Una llamada saliente programada (API, secuencia de onboarding o devolución de llamada) ha terminado o ha fallado, también cuando nadie contestó tras todos los intentos.

Cabeceras de cada entrega

  • Telofia-SignatureFirma: t=<tiempo unix>,v1=<HMAC-SHA256 hex>
  • Telofia-EventTipo de evento, p. ej. appointment.booked
  • Telofia-DeliveryID única de la entrega
  • User-AgentTelofia-Webhooks/1.0 · Identifica a nuestro emisor de webhooks

Entrega y reintentos

  • Responde con cualquier estado 2xx en menos de 10 segundos. No se siguen redirecciones.
  • Las entregas fallidas se reintentan tras 1 min, 5 min, 30 min, 2 h, 6 h y 12 h (7 intentos, unas 21 horas).
  • El mismo evento puede llegar más de una vez. Usa el id del evento para descartar duplicados.
  • Solo se aceptan direcciones https públicas. Tras 50 fallos seguidos, el endpoint se pausa y el panel te indica el motivo.
  • El panel guarda un registro de entregas con códigos de estado y respuestas, y permite reintentar una entrega a mano.
Entrega de ejemplo: 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"
      }
    ]
  }
}

Verificación de firmas. Cada envío incluye una cabecera Telofia-Signature con el formato t=timestamp,v1=signature. Calcula el HMAC-SHA256 de “timestamp.raw_body” con tu secreto de firma y compáralo con v1. Rechaza las marcas de tiempo con más de 5 minutos de antigüedad.

Node.js · Verificar la firma
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 · Verificar la firma
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", ""))
Receptor de ejemplo (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);

Valores estables

Estos valores en las llamadas (API y webhooks call.*) y en las llamadas salientes (outbound_call.finished, /calls/outbound) son un contrato estable: podemos añadir valores nuevos, pero no cambiaremos ni eliminaremos estos. Los intentos salientes sin respuesta, ocupados o rechazados nunca crean una llamada, así que no hay call.completed para ellos: tras el último intento recibes outbound_call.finished con last_error no_answer. Una llamada atendida por el buzón de voz cuenta como atendida (normalmente es una llamada corta con outcome other).

Estado de la llamada

in_progress
La llamada está en curso.
completed
La persona habló con el asistente.
missed
Nadie habló o el asistente no pudo atender la llamada (ver end_reason).
failed
No se pudo gestionar la llamada por un error.

Resultado de la llamada

booked
Se reservó una cita.
transferred
La llamada se transfirió a una persona.
message
Se tomó un mensaje, una solicitud de devolución de llamada, un pedido o un seguimiento.
info
La persona obtuvo información; no hizo falta nada más.
spam
Spam, telemarketing o un número bloqueado.
other
Cualquier otro caso, p. ej. una llamada muy corta o un buzón de voz.

Motivo de fin de la llamada (end_reason)

caller_hangup
La otra persona colgó.
caller_hangup_greeting
Llamada saliente: la persona colgó durante el saludo o justo después, sin decir nada (llamada de hasta 45 s). En ese caso caller_spoke es false.
agent_hangup
El asistente terminó la llamada tras despedirse.
transferred
La llamada se transfirió a una persona.
silence
Terminó tras un silencio prolongado.
max_duration
Se alcanzó la duración máxima de la llamada.
spam
Terminó como spam.
blocked
El número está bloqueado en contactos.
busy
Todas las líneas de la cuenta estaban ocupadas.
over_quota
No quedan minutos de llamada.
trial_expired
La prueba gratuita ha terminado.
inactive
No hay un plan activo.
assistant_not_live
El asistente está en pausa.
no_assistant
Ningún asistente atiende este número.
ai_budget
Atendida sin IA por un límite temporal; se pidió al equipo que devuelva la llamada.
error
Un error técnico terminó la llamada.

Estado de la llamada saliente

scheduled
Esperando su hora (o el siguiente intento).
dialing
Llamando ahora.
done
Atendida; call_id es la conversación.
failed
Sin respuesta tras todos los intentos, o no se pudo hacer (ver last_error).
canceled
Cancelada (API, la persona salió de la secuencia o la secuencia se desactivó).

last_error de la llamada saliente

no_answer
Nadie contestó (también si estaba ocupado o se rechazó).
failed
El asistente no estaba disponible a la hora de la llamada (plan, minutos o pausa).
no_result
Sin resultado de la llamada en 15 minutos.
expired
La llamada llevaba más de 2 horas de retraso, así que no se hizo.
outside_window
Movida al siguiente horario de llamadas (el estado sigue siendo scheduled).
dial_error
La red telefónica rechazó la llamada (se envía como dial_<reason>).
over_quota
No quedan minutos de llamada.
inactive
No hay un plan activo.
assistant_not_live
El asistente está en pausa.
no_outbound_number
No hay un número de empresa desde el que llamar.
canceled_by_api
Cancelada con DELETE /calls/outbound/{id}.
unenrolled
La persona salió de la secuencia (POST /api/outreach/unenroll).
opt_out
La persona pidió no recibir llamadas.
replaced
Sustituida por una devolución de llamada más reciente al mismo número.

Datos en vivo vía MCP

El asistente consulta tu sistema durante la llamada

Conecta un servidor MCP remoto (Model Context Protocol), como el catálogo de tu tienda, el stock o tu sistema de reservas o pedidos. Tú eliges exactamente qué herramientas y recursos (solo lectura) puede usar el asistente.

Aprender por adelantado

Para contenido que cambia poco, como tarifas, descripciones de productos o políticas. Los recursos y herramientas de solo lectura elegidos se importan al conocimiento del asistente cada 1 a 168 horas o cuando lo pidas. Sin retrasos en la llamada.

Consultar en vivo en la llamada

Para lo que cambia: stock, disponibilidad, estado de pedidos. El asistente usa la herramienta durante la conversación con un breve “un momento, lo compruebo”. Si tu servidor no responde en unos 2,5 segundos, dice que ahora no puede confirmarlo y ofrece que tu equipo vuelva a contactar.

Qué necesitas

  • Un servidor MCP accesible por https (Streamable HTTP, p. ej. https://mcp.example.com/mcp; los servidores HTTP+SSE antiguos se detectan automáticamente).
  • Autenticación: ninguna, un token Bearer o una cabecera propia como X-API-Key. Los secretos se cifran (AES-256-GCM) y nunca llegan al navegador.
  • Herramientas de solo lectura. Las marcadas como destructivas se bloquean; para las que no están marcadas como de solo lectura, confirmas que solo leen datos.
  • Hasta 5 fuentes y 50 herramientas en vivo por asistente. Las consultas en vivo deberían responder en unos 2 segundos.
  • Opcional para el widget web: en una herramienta «Al inicio de la llamada», elige el argumento que recibe el token de identidad del visitante con sesión iniciada (ver Widget de voz para tu web).

Los argumentos se validan con el esquema de la herramienta, los resultados se acortan y se tratan estrictamente como datos (nunca como instrucciones), y cada consulta se registra sin datos de quien llama. Se rechazan las direcciones privadas e internas.

Widget de voz para tu web

Tu asistente en tu web, con una línea de código

Los visitantes pulsan un botón y hablan, desde el navegador, con el mismo asistente que atiende tu teléfono. Las llamadas web usan los minutos de tu plan, sin costes de telefonía.

Desde Office

  • Unos 14 KB, sin dependencias, aislado en Shadow DOM para que los estilos de tu web no lo rompan.
  • Habla el idioma de tu página (<html lang>) o el que indiques con data-lang="de".
  • Subtítulos en vivo; si el micrófono está bloqueado, el visitante puede escribir y el asistente responde por voz.
  • Limita el widget a tus dominios y solo se iniciará en esos sitios.
Código para insertar (tu clave está en el panel)
<script src="https://telofia.com/widget.js" data-key="tfw_…" async></script>
API de 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

¿Tienes una Content Security Policy estricta? Permite script-src https://telofia.com https://cdn.jsdelivr.net, connect-src https://telofia.com wss://*.livekit.cloud https://*.livekit.cloud y media-src blob:.

Usuarios con sesión iniciada (token de identidad)

Si los visitantes han iniciado sesión en tu web, el asistente puede saber quién habla sin pedirle el e-mail.

  • Tu servidor emite un token firmado y de corta duración para el usuario con sesión iniciada (p. ej. HMAC, válido 15 minutos). Nunca pongas un e-mail ni un ID de usuario en el navegador: cualquiera podría escribir el de otra persona en la consola.
  • Llama a TelofiaWidget.identify({ token }) en cualquier momento; una llamada posterior sustituye el token e identify(null) lo borra. Antes de que se cargue widget.js, define window.TelofiaIdentity = { token, expires_at }: se lee al empezar la conversación y un token caducado se ignora.
  • En el panel, elige el argumento del token de identidad en tu herramienta MCP «Al inicio de la llamada». Telofia pasa el token sin cambios solo a ese argumento, nunca lo guarda ni lo muestra y lo mantiene fuera de transcripciones, resúmenes, webhooks y registros; el asistente nunca lo ve.
  • De 8 a 400 caracteres: letras, dígitos y . _ ~ + / = - (p. ej. base64url). Renuévalo antes de que caduque, por ejemplo cada 10 minutos. Lo verifica tu servidor MCP; un token no válido o caducado debería dar el mismo resultado que no tener token. Las llamadas telefónicas y las conversaciones sin token funcionan como hasta ahora.
Token de identidad
// 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 telefónico

Tus nuevos clientes reciben una llamada de bienvenida automáticamente

Cuando alguien se registra en tu sistema, envíalo a una secuencia de llamadas. El asistente le llama desde el número de tu empresa, le ayuda a empezar y vuelve a llamar unos días después. Las llamadas se hacen dentro del horario de llamadas de la secuencia: por defecto, de lunes a sábado, de 9:00 a 20:00 en tu zona horaria, o en los días y horas que configures.

POST/api/outreach/enroll

Autentícate con la clave de la secuencia (tlo_…) del panel, como token Bearer o en la cabecera X-Telofia-Key. Cada secuencia tiene su propia clave.

Cuerpo de la petición

  • phonestringobligatorio
    Número de teléfono, idealmente en formato E.164. Los números nacionales se leen con el prefijo del país de tu cuenta.
  • consenttrueobligatorio
    Debe ser true: la persona aceptó ser contactada por teléfono, p. ej. en tu formulario de registro.
  • namestringopcional
    Nombre, hasta 120 caracteres.
  • emailstringopcional
    Dirección de email.
  • contextstringopcional
    Lo que el asistente debe saber de esta persona, hasta 1.000 caracteres (p. ej. el plan elegido).
  • external_idstring | numberopcional
    La ID de la persona en tu sistema (texto o número). La misma ID se inscribe solo una vez.
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
  }
}

Respuestas

  • 201: inscrita. enrollment_id, external_id, status, next_call_at (hora de la primera llamada), intro_sms_at (cuándo sale el SMS previo a la primera llamada, o null) y limits { per_day, remaining_today }
  • 200: ya inscrita (mismo external_id, o este número ya está en curso en la secuencia), con duplicate: true
  • 400: invalid_request, invalid_phone o consent_required
  • 401: invalid_key
  • 409: sequence_disabled, consent_missing u opted_out (la persona no quiere llamadas)
  • 429: limit, se alcanzó el límite diario de inscripciones de la secuencia (500 personas nuevas cada 24 horas; el cuerpo incluye limit). rate_limited: más de 120 peticiones por minuto con esta clave (cabecera Retry-After)

Horario de llamadas: en el panel, elige para cada secuencia los días y una franja horaria entre las 7:00 y las 21:00. Si nadie contesta, el asistente lo intenta hasta 3 veces, con 2 horas de diferencia, dentro de ese horario.

SMS antes de la llamada: si está activado en la secuencia (panel), la persona recibe un mensaje breve desde el número desde el que llamará el asistente, por defecto 20 minutos antes de la primera llamada (5–120 min). Si la primera llamada fuera antes, se aplaza para que el SMS siempre salga primero, dentro del horario de llamadas. Un SMS fallido (la persona respondió STOP, se agotó el límite de SMS) nunca detiene la llamada. Cada SMS cuenta para tu límite de SMS y aparece en el panel, en SMS.

Saludo: en el panel defines la primera frase de la llamada para cada paso (o por defecto para toda la secuencia): un texto fijo con las variables {first_name}, {first_name_vocative} (vocativo polaco, p. ej. Krystianie), {assistant_name}, Telofia (nombre de la empresa en las llamadas, de los ajustes del asistente) y {name}, con una variante aparte para registros sin nombre, o un modo en el que el asistente redacta la primera frase a partir de name, context, el objetivo del paso y el resultado de las herramientas «Al inicio de la llamada». Que llama un asistente de IA y que la llamada se graba se añade siempre a la primera frase si el texto no lo dice.

Sacar a una persona de la secuencia

POST/api/outreach/unenroll

Cuando alguien ya no necesita las llamadas (p. ej., tras su primera venta o porque se dio de baja), detén su secuencia con la misma clave. Las llamadas programadas se cancelan al momento. Una llamada ya en curso no se interrumpe, pero después no habrá más llamadas.

  • external_id | enrollment_id | phoneobligatorio
    Exactamente uno de: external_id (el que enviaste al inscribir), enrollment_id (de la respuesta de inscripción) o phone.
  • reasonopcional
    Motivo opcional, hasta 200 caracteres, que se guarda en el registro de auditoría de tu cuenta.
  • 200: detenida (changed: true) o ya finalizada: status stopped, completed o failed (changed: false). Se puede repetir sin riesgo.
  • 400: invalid_request (sin identificador o con más de uno) o invalid_phone
  • 401: invalid_key
  • 404: not_found, no existe esa inscripción en esta secuencia
  • 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
}

Resultados de las llamadas en los webhooks

call.started y call.completed de las llamadas de una secuencia incluyen callback_id, external_id y outreach { sequence_id, enrollment_id, external_id, step } (todos null en las llamadas entrantes). Los intentos sin respuesta no crean una llamada, así que no hay call.completed para ellos: tras el último intento recibes outbound_call.finished con status failed y last_error no_answer. Los reintentos de un mismo paso cuentan como un solo resultado. call.completed incluye también caller_spoke y caller_words (cuántas palabras dijo la persona). Si alguien contestó y colgó durante el saludo o justo después sin decir nada, end_reason es caller_hangup_greeting.

call.completed de una llamada de secuencia
{
  "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
}

Calendario, CRM y automatización

Integraciones listas para usar, sin código

  • Google Calendar

    El asistente consulta la disponibilidad real y reserva, mueve y cancela citas en tu calendario. Solo lee las horas ocupadas, nunca los títulos de los eventos.

  • HubSpot

    Cada llamada se registra en el contacto, las reservas se convierten en reuniones y las devoluciones de llamada en tareas. Opcionalmente crea contactos y negocios.

  • Pipedrive

    Llamadas como actividades o notas, reservas como reuniones, devoluciones de llamada como actividades. Opcionalmente crea personas, tratos o leads.

  • Zapier, Make y n8n

    Guías paso a paso en el panel. Funcionan con webhooks y la API, incluidos en todos los planes.

Planes y límites

Qué plan necesitas

Límites por cuenta: peticiones por minuto (también por clave, ventana deslizante) y por día (UTC). Por encima del límite recibes 429 rate_limited con la cabecera Retry-After.

PlanSolicitudes / minSolicitudes / díaClaves APIWebhooks
Prueba gratuita (acceso de prueba)20100011
Line30200022
Desk6010.00055
Office18050.0001010
Network600200.0002520

Widget de voz para tu web: desde el plan Office.

¿Necesitas límites más altos? Escríbenos y podemos ampliarlos para tu cuenta.

Acceso anticipado: sé de los primeros negocios en dejar que la IA conteste el teléfono.

¿Listo para conectar?

Empieza la prueba gratuita, crea una clave de API de prueba y envía tu primera petición en minutos. La API está incluida en todos los planes, desde Line.