Para negocios y desarrolladores
Conecta tu asistente de IA
Deja que tus clientes chateen con tu asistente de IA aquí mismo, en FastReply. Tu IA sigue funcionando en tus servidores: le entregamos cada mensaje del cliente y mostramos su respuesta en el chat.
- Funciona con cualquier IA o chatbot
- Un desarrollador lo configura en una tarde
- Tu WhatsApp sigue igual
Cómo funciona
- Un cliente escribe a tu negocio en FastReply.
- FastReply envía el mensaje a la dirección web de tu bot (el endpoint), firmado para que tu bot sepa que viene de nosotros.
- Tu bot pide una respuesta a tu IA.
- Tu bot envía la respuesta a la API de FastReply y el cliente la ve en el chat.
Tu bot no tiene que responder dentro de la misma petición: confirma el mensaje al momento y responde cuando la IA esté lista. Los modelos de IA lentos no son un problema.
Seguridad y tus datos
FastReply es la app de chat que usan tus clientes, así que recibe sus mensajes y los pasa a tu endpoint, como cualquier plataforma de chat o de atención al cliente. Los mensajes no tienen cifrado de extremo a extremo: FastReply puede leerlos técnicamente. Esto es lo que vemos, lo que guardamos y cómo lo protegemos.
- Qué recibe tu bot
- El texto del mensaje, una referencia seudónima del cliente (la misma para tu negocio, distinta para cualquier otro) y el idioma del cliente. Su nombre, solo si decide compartirlo en ese chat. Nunca su correo electrónico.
- Qué guardamos y durante cuánto tiempo
- Guardamos los mensajes para que los clientes vean su historial, y los borramos a los 12 meses. Cuando un cliente borra un chat o su cuenta, borramos sus mensajes en el momento y te enviamos
conversation.closedpara que borres tu copia. Las copias de seguridad diarias de la base de datos se guardan 14 días, así que lo borrado desaparece de ellas en dos semanas. - Quién puede leer las conversaciones
- El cliente y tu endpoint. En el panel, tu equipo solo ve sus propios chats de prueba de «Probar». Nuestros moderadores solo ven un mensaje cuando un cliente lo denuncia, junto con el anterior para tener contexto. Quienes gestionan FastReply tienen acceso técnico a la base de datos; solo abrimos una conversación para atender una denuncia, una petición de soporte o una obligación legal. No vendemos los mensajes ni los usamos para publicidad ni para entrenar IA.
- En tránsito
- Todo viaja por HTTPS. Solo llamamos a tu endpoint por HTTPS, nunca seguimos redirecciones, rechazamos direcciones de redes privadas e internas y desistimos a los 10 segundos.
- Prueba de que somos nosotros
- Cada evento va firmado con HMAC-SHA256 y una marca de tiempo, así que tu endpoint puede rechazar peticiones que no enviamos nosotros y repeticiones de más de 5 minutos. Cómo comprobar la firma.
- Tus claves
- De tu clave de API solo guardamos un hash: nadie en FastReply puede verla y no podemos volver a mostrártela. Si la pierdes, sustitúyela. Tu secreto de firma está cifrado en nuestra base de datos (AES-256-GCM). Las claves de prueba solo llegan a chats de prueba, y las reales solo a chats reales.
- Dónde están los datos
- En la Unión Europea: los servidores, la base de datos y sus copias de seguridad.
- ¿Has encontrado un problema de seguridad?
- Escríbenos a contact@expressai.bot y, por favor, danos tiempo para arreglarlo antes de hacerlo público. Los contactos de seguridad también están en nuestro security.txt.
Para dueños de negocios
No necesitas programar. Esto es lo que necesitas y lo que haces en FastReply.
Qué necesitas
- Un asistente de IA o chatbot que pueda atender a tus clientes: hecho por tu equipo, una agencia o una plataforma de chatbots, con cualquier modelo de IA.
- Alguien que le añada un pequeño endpoint web: tu desarrollador, agencia o proveedor del chatbot. Envíales esta página; la parte técnica empieza en «Guía rápida para desarrolladores».
- Acceso de propietario a tu negocio en FastReply.
Qué haces en el panel
- Abre tu panel, elige tu negocio y ve a Chat en la app. Pega la URL del endpoint que te dé tu desarrollador y guárdala. Mostramos un secreto de firma una sola vez: pásaselo a tu desarrollador de forma segura.
- Pulsa Verificar ahora. Enviamos un evento de prueba; cuando tu bot responde bien, la conexión queda verificada.
- Acepta las condiciones de tratamiento de datos y pulsa Obtener clave de API. Pásale la clave a tu desarrollador: tu bot la usa para responder a los clientes.
- Pulsa Obtener clave de prueba y abre un chat de prueba. Chatea con tu bot como lo haría un cliente; solo tu equipo ve estos chats.
Trata la clave de API y el secreto de firma como contraseñas: compártelos solo con quien configura tu bot, nunca en chats públicos ni listas de correo. Si uno se filtra, sustitúyelo en Claves y conexión; el anterior deja de funcionar al instante.
FastReply nunca automatiza WhatsApp. Tus clientes pueden seguir escribiéndote allí como siempre; el chat en la app es una forma más de llegar a tu IA.
Guía rápida para desarrolladores
Añadirás un endpoint HTTPS que recibe eventos firmados de FastReply y llamarás a la API de FastReply para responder. Sirve cualquier lenguaje o framework. Los ejemplos usan Python; en «Ejemplo completo» tienes también un bot en Node.js.
1. Crea un endpoint HTTPS
Añade a tu servidor una URL que acepte peticiones POST con un cuerpo JSON, por ejemplo https://tu-bot.example/fastreply. Debe usar https://, ser accesible desde internet y responder directamente: rechazamos las redirecciones y las direcciones privadas o internas.
¿Desarrollas en tu ordenador? Expón tu servidor local con un túnel como Cloudflare Tunnel o ngrok y usa su dirección https:// como endpoint.
2. Comprueba que cada evento viene de FastReply
Cada evento lleva tres cabeceras. Calcula un HMAC-SHA256 de timestamp.raw_body con tu secreto de firma y compáralo con la firma. Responde 401 si no coincide o si el timestamp tiene más de 5 minutos.
| Cabecera | Significado |
|---|---|
| X-FastReply-Event-Id | Id único del evento. Los reintentos lo repiten: úsalo para ignorar duplicados. |
| X-FastReply-Timestamp | Hora Unix en segundos en que firmamos el evento. |
| X-FastReply-Signature | v1= seguido del HMAC-SHA256 en hexadecimal de timestamp.raw_body. |
import hashlib, hmac, time
def valid_signature(raw_body: bytes, timestamp: str, signature: str, secret: str) -> bool:
# Reject missing or old timestamps (more than 5 minutes) to stop replayed events.
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
return False
signed = f"{timestamp}.".encode() + raw_body # the raw bytes, not re-encoded JSON
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature.removeprefix("v1="), expected)Firma el cuerpo de la petición tal cual llega. Si analizas el JSON y lo vuelves a codificar, los bytes cambian y la firma no coincidirá.
3. Responde al desafío de verificación
Cuando guardas o verificas tu endpoint, enviamos un evento endpoint.verify. Comprueba su firma y responde 200 con el mismo valor de challenge en un cuerpo JSON.
{
"type": "endpoint.verify",
"id": "evt_01J...",
"created_at": "2026-09-25T10:00:00Z",
"data": { "challenge": "b3f1c9..." }
}HTTP/1.1 200 OK
Content-Type: application/json
{ "challenge": "b3f1c9..." }4. Recibe los mensajes de los clientes
Cada mensaje de un cliente llega como un evento message.created:
{
"type": "message.created",
"id": "evt_01J...",
"created_at": "2026-09-25T10:01:02Z",
"data": {
"business_id": "biz_01J...",
"conversation": { "id": "conv_01J...", "is_new": false, "is_test": false },
"customer": { "ref": "cus_7f3a...", "locale": "es-ES", "display_name": null },
"message": { "id": "msg_01J...", "type": "text", "text": "Do you have a table for 2 tonight?" }
}
}- Responde 2xx en menos de 10 segundos, antes de llamar a tu IA. Haz el trabajo de la IA en segundo plano y envía la respuesta en el paso 5.
- Si no recibimos un 2xx a tiempo, reintentamos a los 2 s, 5 s, 15 s, 1 min y 5 min con el mismo id de evento; después nos rendimos y ofrecemos WhatsApp al cliente.
- Guarda el historial de cada chat por
conversation.id.customer.refes un seudónimo estable;display_namesolo viene si el cliente decidió compartir su nombre. conversation.is_testes true en los chats de prueba de tu panel. Respóndelos con tu clave de prueba (fr_test_…).
5. Envía la respuesta
Publica la respuesta de tu IA en la conversación con tu clave de API en la cabecera Authorization. Usa la clave real (fr_live_…) con los clientes y la de prueba con los chats de prueba.
curl -X POST "https://api.fastreply.online/v1/business/conversations/conv_01J.../messages" \
-H "Authorization: Bearer $FASTREPLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "I can hold a table for 2 at 19:30 or 21:00. Which one?",
"reply_to": "msg_01J...",
"quick_replies": ["19:30", "21:00"],
"client_message_id": "msg_01J..."
}'| Campo | Reglas |
|---|---|
| text | Obligatorio. Texto plano, de 1 a 4.000 caracteres. El HTML y el Markdown se muestran tal cual. |
| reply_to | Opcional. El id del mensaje del cliente al que respondes; lo usamos para medir tu tiempo de respuesta. |
| quick_replies | Opcional. Hasta 3 sugerencias pulsables de hasta 25 caracteres cada una. |
| sender | bot (por defecto) o staff cuando escribe una persona de tu equipo. |
| client_message_id | Opcional. Si repites el mismo valor en 24 horas, recibes el mensaje original en vez de un duplicado, así que reintentar es seguro. |
Para comprobar una clave, llama a /v1/business/me: devuelve el id de tu negocio, el estado de la conexión y si la clave es real o de prueba.
curl "https://api.fastreply.online/v1/business/me" \
-H "Authorization: Bearer $FASTREPLY_API_KEY"6. Conecta y prueba
Despliega tu endpoint y conéctalo en el panel: guarda la URL y el secreto de firma, verifica, obtén la clave de API y después una clave de prueba, y abre un chat de prueba. Cuando el chat de prueba funcione, tus clientes podrán chatear con tu bot.
Ejemplo completo
Un bot pequeño que funciona. Pon tus claves en variables de entorno, sustituye ask_your_ai por una llamada a tu IA, despliégalo y usa su URL /fastreply como endpoint.
FASTREPLY_SIGNING_SECRET=whsec_... # shown once when you save the endpoint
FASTREPLY_API_KEY=fr_live_... # answers real customers
FASTREPLY_TEST_KEY=fr_test_... # answers "Try it" chats from your dashboardPython (FastAPI)
# bot.py: pip install fastapi uvicorn httpx; run with: uvicorn bot:app --port 8080
import hashlib, hmac, json, os, time
import httpx
from fastapi import BackgroundTasks, FastAPI, Request, Response
from fastapi.responses import JSONResponse
API_URL = "https://api.fastreply.online"
SIGNING_SECRET = os.environ["FASTREPLY_SIGNING_SECRET"]
LIVE_KEY = os.environ["FASTREPLY_API_KEY"]
TEST_KEY = os.environ.get("FASTREPLY_TEST_KEY", "")
app = FastAPI()
seen_events: set[str] = set() # use your database or Redis in production
def ask_your_ai(text: str, conversation_id: str) -> str:
# Call your AI here (any model or chatbot platform) and return plain text.
# Use conversation_id to keep each customer's chat history.
return f"Thanks! You said: {text}"
def valid_signature(raw_body: bytes, timestamp: str, signature: str) -> bool:
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
return False
signed = f"{timestamp}.".encode() + raw_body
expected = hmac.new(SIGNING_SECRET.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(signature.removeprefix("v1="), expected)
def answer(event: dict) -> None:
conversation, message = event["data"]["conversation"], event["data"]["message"]
key = TEST_KEY if conversation["is_test"] else LIVE_KEY
path = f"/v1/business/conversations/{conversation['id']}"
with httpx.Client(base_url=API_URL, headers={"Authorization": f"Bearer {key}"}, timeout=10) as api:
api.post(f"{path}/typing") # shows "typing..." while your AI thinks
text = ask_your_ai(message["text"], conversation["id"])
api.post(f"{path}/messages", json={
"text": text[:4000],
"reply_to": message["id"],
"client_message_id": message["id"], # safe to retry
}).raise_for_status()
@app.post("/fastreply")
async def webhook(request: Request, background: BackgroundTasks) -> Response:
raw_body = await request.body()
timestamp = request.headers.get("X-FastReply-Timestamp", "")
signature = request.headers.get("X-FastReply-Signature", "")
if not valid_signature(raw_body, timestamp, signature):
return Response(status_code=401)
event = json.loads(raw_body)
if event["type"] == "endpoint.verify":
return JSONResponse({"challenge": event["data"]["challenge"]})
if event["id"] in seen_events: # a retry we already handled
return Response(status_code=204)
seen_events.add(event["id"])
if event["type"] == "message.created":
background.add_task(answer, event) # runs after we acknowledge
elif event["type"] == "conversation.closed":
pass # delete anything you stored for event["data"]["conversation"]["id"]
return Response(status_code=202)Node.js (Express)
// bot.mjs: npm install express; run with: node bot.mjs (Node 18 or newer)
import crypto from "node:crypto";
import express from "express";
const API_URL = "https://api.fastreply.online";
const { FASTREPLY_SIGNING_SECRET, FASTREPLY_API_KEY, FASTREPLY_TEST_KEY } = process.env;
const app = express();
const seenEvents = new Set(); // use your database or Redis in production
async function askYourAi(text, conversationId) {
// Call your AI here (any model or chatbot platform) and return plain text.
// Use conversationId to keep each customer's chat history.
return `Thanks! You said: ${text}`;
}
function validSignature(rawBody, timestamp = "", signature = "") {
if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = crypto
.createHmac("sha256", FASTREPLY_SIGNING_SECRET)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
const given = signature.replace(/^v1=/, "");
return given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}
async function answer(event) {
const { conversation, message } = event.data;
const key = conversation.is_test ? FASTREPLY_TEST_KEY : FASTREPLY_API_KEY;
const call = (action, body) =>
fetch(`${API_URL}/v1/business/conversations/${conversation.id}/${action}`, {
method: "POST",
headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
body: body ? JSON.stringify(body) : undefined,
});
await call("typing"); // shows "typing..." while your AI thinks
const text = await askYourAi(message.text, conversation.id);
const response = await call("messages", {
text: text.slice(0, 4000),
reply_to: message.id,
client_message_id: message.id, // safe to retry
});
if (!response.ok) console.error("reply failed", response.status, await response.text());
}
// express.raw keeps the exact bytes we signed.
app.post("/fastreply", express.raw({ type: "*/*" }), (req, res) => {
const rawBody = Buffer.isBuffer(req.body) ? req.body : Buffer.alloc(0);
if (!validSignature(rawBody, req.get("X-FastReply-Timestamp"), req.get("X-FastReply-Signature"))) {
return res.sendStatus(401);
}
const event = JSON.parse(rawBody.toString("utf8"));
if (event.type === "endpoint.verify") return res.json({ challenge: event.data.challenge });
if (seenEvents.has(event.id)) return res.sendStatus(204); // a retry we already handled
seenEvents.add(event.id);
if (event.type === "message.created") answer(event).catch(console.error); // after we acknowledge
// conversation.closed: delete anything you stored for event.data.conversation.id
res.sendStatus(202);
});
app.listen(8080);Deja la llamada a la IA donde la pone el ejemplo: después de confirmar el evento. Una respuesta que tarda más de 10 segundos no es un problema, pero una entrega confirmada después de 10 segundos cuenta como fallida.
Antes de publicar
- Comprueba la firma de cada evento y rechaza los timestamps de más de 5 minutos.
- Responde 2xx en menos de 10 segundos; llama a tu IA después.
- Ignora los eventos cuyo
X-FastReply-Event-Idya hayas procesado. - Responde a los chats de prueba (
is_test: true) con la clave de prueba y a los clientes con la clave real. - Envía texto plano de hasta 4.000 caracteres y como mucho 3 respuestas rápidas.
- Llama a
/typingmientras tu IA trabaja, para que el cliente vea «escribiendo…». - Cuando la IA no pueda ayudar, llama a
/handoffy deja que responda una persona consender: staff. - Con
conversation.closed, borra lo que guardaste de esa conversación. - Mantén la clave de API y el secreto de firma fuera de tu repositorio de código y de tus logs.
- No pases de 50 peticiones por segundo por clave; tras un
429, espera los segundos deRetry-After.
Solución de problemas
Las entregas fallidas de los últimos 7 días aparecen en Errores de entrega en tu panel, con el motivo.
- La verificación falló: rechazó nuestra firma
- Comprueba que el bot usa el secreto de firma más reciente y que firma el cuerpo tal cual llega, no el JSON vuelto a codificar.
- Sin respuesta en 10 segundos
- Comprueba que el endpoint está desplegado y es accesible desde internet, y que responde antes de llamar a tu IA.
- Redirigió
- Usa la dirección final, por ejemplo con o sin barra final o
www, tal como la espera tu servidor. - Apunta a una dirección privada o interna
- No podemos llegar a localhost ni a direcciones de redes privadas. Despliega el bot o usa un túnel.
- No devolvió el desafío
- Responde a
endpoint.verifycon estado 200 y un cuerpo JSON con el mismo valor dechallenge. - 401 invalid_api_key
- La clave es incorrecta o se sustituyó. Copia la clave actual en la configuración de tu bot.
- 404 conversation_not_found
- Suele ser el tipo de clave equivocado: los chats de prueba solo aceptan la clave de prueba y los de clientes solo la real.
- 403 blocked, integration_disabled o conversation_not_owned
- El cliente bloqueó tu negocio, la conexión está desactivada o la conversación es de otro negocio. No reintentes.
- A los clientes se les ofrece WhatsApp
- Tras 20 entregas fallidas seguidas, el chat en la app se pausa hasta que tu bot vuelva a responder. Corrige el error que muestra el panel y vuelve a verificar.
Referencia de la API
URL base: https://api.fastreply.online. Autentica cada petición con Authorization: Bearer y tu clave de API.
API para negocios
| Petición | Qué hace |
|---|---|
| POST /v1/business/conversations/{id}/messages | Envía una respuesta. Devuelve 201 con el id del mensaje. |
| POST /v1/business/conversations/{id}/typing | Muestra «escribiendo…» durante 8 segundos como mucho, o hasta que llegue tu respuesta. Devuelve 204. |
| POST /v1/business/conversations/{id}/handoff | Pasa el chat a tu equipo, con una note opcional de hasta 500 caracteres. El cliente ve que le responderá una persona del equipo. Devuelve 204. |
| GET /v1/business/me | Comprueba tu clave: id del negocio, estado de la conexión, prefijo de la clave y modo (real o de prueba). |
Eventos que enviamos a tu endpoint
| Evento | Cuándo |
|---|---|
| endpoint.verify | Guardas o verificas tu endpoint. Devuelve el desafío. |
| message.created | Un cliente envió un mensaje. |
| conversation.closed | El cliente borró el chat o su cuenta, o desconectaste tu bot. Borra los datos de la conversación. |
Errores
Los errores llegan como JSON con un code y un message dentro de error. Códigos: invalid_request, text_too_long, too_many_quick_replies (400), invalid_api_key (401), integration_disabled, conversation_not_owned, blocked (403), conversation_not_found (404), conversation_closed (409) y rate_limited (429).
¿Te has atascado o quieres que revisemos tu configuración? Escríbenos a contact@expressai.bot.