Resumen

Envía mensajes de WhatsApp desde tu sistema con tu propio número: confirmaciones de pedido, comprobantes en PDF, recordatorios de pago o avisos de despacho. Y recibe en tu sistema lo que te responden tus clientes.

  1. Vinculas tu número una sola vez escaneando un QR en el portal (como WhatsApp Web).
  2. Tu sistema llama a la API con tu token.
  3. El mensaje sale desde tu número; tu celular sigue funcionando normal.

URL base: https://whatsapp.holainge.com · Comprueba que responde: GET https://whatsapp.holainge.com/salud

No es la API oficial de WhatsApp: WhatsApp puede bloquear números que envían spam. Lee las condiciones de uso y las buenas prácticas antes de enviar en Producción.

Antes de empezar

  1. Contrata un plan de la API WhatsApp.
  2. En Mis suscripciones > API WhatsApp pulsa Vincular número y escanea el QR desde WhatsApp > Dispositivos vinculados.
  3. Crea un token de Desarrollo para integrar sin enviar nada, y uno de Pruebas para enviarte mensajes a ti mismo.
  4. Cuando todo funcione, crea un token de Producción.

Entornos

Entorno del tokenQué pasa al enviarConsumo
DesarrolloNo se envía nada a WhatsApp. La respuesta es real pero con estado Simulado. Ideal para programar.Límite de pruebas
PruebasSe envía de verdad, solo a tu propio número vinculado.Límite de pruebas
ProducciónSe envía a cualquier número.Límite mensual del plan

Cada mensaje aceptado cuenta 1 en el consumo del mes. Si WhatsApp lo rechaza (por ejemplo, el número no existe), no se cuenta.

Endpoints

MétodoRutaDescripción
POST/api/v1/mensajes/textoEnvía un mensaje de texto.
POST/api/v1/mensajes/imagenEnvía una imagen (JPG, PNG o WEBP) con texto opcional.
POST/api/v1/mensajes/documentoEnvía un documento (PDF, XML, Excel, Word, ZIP...).
GET/api/v1/mensajes/{id}Consulta un mensaje y su estado (enviado, entregado, leído).
GET/api/v1/mensajes?cantidad=50Últimos mensajes del entorno del token.
GET/api/v1/estadoVerifica el token y si tu número está conectado.

Todas las rutas exigen la cabecera Authorization: Bearer <token>. Puedes enviar Idempotency-Key: <tu-id> para reintentar sin duplicar: con la misma clave te devolvemos el mismo mensaje (si el envío falló, la clave se libera para que puedas reintentar).

Enviar texto

POST https://whatsapp.holainge.com/api/v1/mensajes/texto
curl -X POST https://whatsapp.holainge.com/api/v1/mensajes/texto \
  -H "Authorization: Bearer inr_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-1520-confirmacion" \
  -d '{
    "numero": "51924246085",
    "texto": "Hola Juan, tu pedido #1520 fue confirmado. Llegará mañana entre 9 y 12."
  }'
  • numero: con código de país (Perú: 51 + 9 dígitos). También aceptamos 924246085 o +51 924 246 085.
  • texto: hasta 4096 caracteres. Admite el formato de WhatsApp: *negrita*, _cursiva_, emojis y saltos de línea (\n).

Enviar imagen o PDF

Envía el archivo de una de estas dos formas (no ambas):

  • url: dirección https pública del archivo; nosotros lo descargamos.
  • base64: el contenido del archivo en Base64 (con nombreArchivo si es documento).

Tamaño máximo: 8 MB.

POST https://whatsapp.holainge.com/api/v1/mensajes/documento
{
  "numero": "51924246085",
  "url": "https://tusistema.com/comprobantes/F001-123.pdf",
  "nombreArchivo": "F001-123.pdf",
  "texto": "Hola Juan, te enviamos tu factura F001-123. ¡Gracias por tu compra!"
}
POST https://whatsapp.holainge.com/api/v1/mensajes/imagen
{
  "numero": "51924246085",
  "base64": "iVBORw0KGgoAAAANSUhEUgAA...",
  "texto": "Así quedó tu pedido 📦"
}

Respuesta y estados

201 si el mensaje se envió (o se simuló), 200 si es una repetición con la misma Idempotency-Key, 422 si WhatsApp lo rechazó:

{
  "id": 1520,
  "tipo": "texto",
  "numero": "51924246085",
  "entorno": "Produccion",
  "estado": "Enviado",
  "error": null,
  "fecha": "2026-11-14T10:30:00-05:00",
  "actualizadoEn": "2026-11-14T10:30:02-05:00",
  "repetido": false
}
EstadoSignificado
EnviadoWhatsApp recibió el mensaje.
EntregadoLlegó al celular del destinatario (dos checks).
LeidoEl destinatario lo abrió (checks azules; solo si tiene activada la confirmación de lectura).
SimuladoToken de Desarrollo: no se envió.
ErrorNo se envió. El campo error explica por qué (ej. "Ese número no tiene WhatsApp").

Si tu número no está conectado, la API responde 400 con regla_negocio y no cuenta consumo.

Estado del número

GET https://whatsapp.holainge.com/api/v1/estado
{
  "api": "WHATSAPP",
  "entorno": "Produccion",
  "descripcionEntorno": "Producción: se envían a cualquier número.",
  "conectado": true,
  "estadoNumero": "Conectado",
  "numero": "51924246085",
  "limiteSolicitudesMinuto": 10,
  "limiteMensual": 3000,
  "horaServidor": "2026-11-14T10:30:00-05:00"
}

estadoNumero: SinVincular, EsperandoQr, Conectado o Desconectado.

Webhook

Configura tu URL en Mis suscripciones > API WhatsApp > Webhook. Te enviamos un POST con JSON en cada evento. Responde con cualquier código 2xx en menos de 10 segundos; si no, reintentamos a los 1, 5, 15, 60 y 240 minutos.

EventoCuándo
mensaje.recibidoUn cliente te escribió.
mensaje.estadoUn mensaje que enviaste fue entregado o leído.
conexionTu número se conectó o se desconectó.
pruebaPulsaste "Enviar evento de prueba" en el portal.

mensaje.recibido

{
  "evento": "mensaje.recibido",
  "fecha": "2026-11-14T10:31:00-05:00",
  "datos": {
    "id": "3EB0C767D26A8B1F",
    "de": "51987654321",
    "nombre": "Juan Pérez",
    "tipo": "texto",
    "texto": "Hola, ¿a qué hora llega mi pedido?",
    "fechaMensaje": "2026-11-14T10:30:58-05:00"
  }
}

tipo: texto, imagen, documento, audio, video, sticker, ubicacion, contacto, reaccion u otro. Los mensajes de grupos no se reenvían.

mensaje.estado

{ "evento": "mensaje.estado", "fecha": "...", "datos": { "id": 1520, "estado": "Leido" } }

conexion

{ "evento": "conexion", "fecha": "...", "datos": { "conectado": false, "numero": "51924246085" } }

Validar la firma

Cada aviso trae estas cabeceras:

  • X-Inercia-Evento: el tipo de evento.
  • X-Inercia-Id: identificador único del aviso (úsalo para ignorar repetidos).
  • X-Inercia-Marca-Tiempo: hora Unix en segundos.
  • X-Inercia-Firma: HEX( HMAC-SHA256( secreto, marcaTiempo + "." + cuerpo ) ) en minúsculas.

Calcula la firma con el cuerpo tal como llegó (sin volver a serializar el JSON) y compárala. Rechaza avisos con una marca de tiempo de más de 5 minutos.

// ASP.NET Core (C#)
app.MapPost("/webhooks/whatsapp", async (HttpRequest req) =>
{
    using var lector = new StreamReader(req.Body);
    var cuerpo = await lector.ReadToEndAsync();
    var marca = req.Headers["X-Inercia-Marca-Tiempo"].ToString();
    var firma = req.Headers["X-Inercia-Firma"].ToString();

    var secreto = Environment.GetEnvironmentVariable("INERCIA_WEBHOOK_SECRETO")!;
    var esperada = Convert.ToHexString(HMACSHA256.HashData(
        Encoding.UTF8.GetBytes(secreto), Encoding.UTF8.GetBytes($"{marca}.{cuerpo}"))).ToLowerInvariant();

    if (!CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(esperada), Encoding.UTF8.GetBytes(firma))
        || Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - long.Parse(marca)) > 300)
        return Results.Unauthorized();

    // ... procesa el evento (en segundo plano si tarda) ...
    return Results.Ok();
});
// Node.js (Express): usa express.raw para tener el cuerpo original
app.post("/webhooks/whatsapp", express.raw({ type: "application/json" }), (req, res) => {
  const marca = req.get("X-Inercia-Marca-Tiempo");
  const esperada = crypto.createHmac("sha256", process.env.INERCIA_WEBHOOK_SECRETO)
    .update(`${marca}.${req.body}`).digest("hex");
  if (esperada !== req.get("X-Inercia-Firma")) return res.sendStatus(401);
  const evento = JSON.parse(req.body);
  res.sendStatus(200);
});

Ejemplo en C#

using System.Net.Http.Json;

var http = new HttpClient { BaseAddress = new Uri("https://whatsapp.holainge.com/") };
http.DefaultRequestHeaders.Authorization = new("Bearer", Environment.GetEnvironmentVariable("INERCIA_TOKEN"));

var solicitud = new HttpRequestMessage(HttpMethod.Post, "api/v1/mensajes/texto")
{
    Content = JsonContent.Create(new { numero = "51924246085", texto = "Tu pedido #1520 está en camino 🚚" })
};
solicitud.Headers.Add("Idempotency-Key", "pedido-1520-despacho");

var respuesta = await http.SendAsync(solicitud);
var cuerpo = await respuesta.Content.ReadAsStringAsync();
Console.WriteLine($"{(int)respuesta.StatusCode}: {cuerpo}");

Buenas prácticas (evita bloqueos)

Esta API conecta tu número como un dispositivo vinculado. WhatsApp puede bloquear números que se comportan como spam, así que:

  • Usa un número dedicado a tu negocio, con su foto y nombre de perfil.
  • Escribe solo a clientes que te dieron su número y esperan tu mensaje (pedidos, comprobantes, citas, cobranzas).
  • No envíes publicidad masiva ni a listas compradas.
  • Personaliza los mensajes (nombre, número de pedido) en lugar de enviar el mismo texto a todos.
  • Respeta el límite por minuto de tu plan: ya añadimos una pequeña pausa entre envíos para que se parezcan a los de una persona.
  • Si un cliente pide que no le escribas más, quítalo de tus envíos.

Estas reglas forman parte de las condiciones de uso del servicio API WhatsApp que aceptaste al registrarte.