Marketer‑friendly
Whapi.co — Documentación de API
WhatsApp API lista para producción: envía, recibe por webhook y administra instancias & grupos.
200 OK 429 Rate limit 500 Error
Base URL
http://<tu-host>:3000
Auth
Body JSON con instanceId + token
Webhook
POST JSON a tu webhook_url por instancia

Índice

POST
/api/send-message
Envía tu primer mensaje en 30s
Curl
PHP
Node.js
JavaScript
Python
Java
Ruby
VB.NET
C#
Go
C
Clojure
Dart
Swift
Objective-C
PowerShell
Shell
curl -X POST http://<tu-host>:3000/api/send-message \
  -H 'Content-Type: application/json' \
  -d '{
    "instanceId":33,
    "token":"TOKEN",
    "to":"+573001112233",
    "type":"text",
    "message":"Hola 👋"
  }'
<?php
$ch = curl_init('http://<tu-host>:3000/api/send-message');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
  'instanceId' => 33,
  'token' => 'TOKEN',
  'to' => '+573001112233',
  'type' => 'text',
  'message' => 'Hola 👋'
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_exec($ch);
?>
import fetch from 'node-fetch';
fetch('http://<tu-host>:3000/api/send-message', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ instanceId:33, token:'TOKEN', to:'+573001112233', type:'text', message:'Hola 👋' })
});
fetch('http://<tu-host>:3000/api/send-message', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ instanceId:33, token:'TOKEN', to:'+573001112233', type:'text', message:'Hola 👋' })
});
import requests
requests.post('http://<tu-host>:3000/api/send-message',
              json={'instanceId':33,'token':'TOKEN','to':'+573001112233','type':'text','message':'Hola 👋'})
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("http://<tu-host>:3000/api/send-message"))
  .header("Content-Type", "application/json")
  .POST(HttpRequest.BodyPublishers.ofString("{\"instanceId\":33,\"token\":\"TOKEN\",\"to\":\"+573001112233\",\"type\":\"text\",\"message\":\"Hola 👋\"}"))
  .build();
client.send(request, HttpResponse.BodyHandlers.ofString());
require 'net/http'
require 'json'
Net::HTTP.post URI('http://<tu-host>:3000/api/send-message'),
  {instanceId:33, token:'TOKEN', to:'+573001112233', type:'text', message:'Hola 👋'}.to_json,
  "Content-Type" => "application/json"
Imports System.Net
Imports System.Text
Dim data = "{\"instanceId\":33,\"token\":\"TOKEN\",\"to\":\"+573001112233\",\"type\":\"text\",\"message\":\"Hola 👋\"}"
Dim client As New WebClient()
client.Headers(HttpRequestHeader.ContentType) = "application/json"
client.UploadString("http://<tu-host>:3000/api/send-message", data)
using var client = new HttpClient();
var content = new StringContent("{\"instanceId\":33,\"token\":\"TOKEN\",\"to\":\"+573001112233\",\"type\":\"text\",\"message\":\"Hola 👋\"}", Encoding.UTF8, "application/json");
await client.PostAsync("http://<tu-host>:3000/api/send-message", content);
package main
import ("bytes"; "net/http")
func main() {
  http.Post("http://<tu-host>:3000/api/send-message", "application/json",
    bytes.NewBuffer([]byte(`{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"text","message":"Hola 👋"}`)))
}
#include <curl/curl.h>
int main(){
  CURL *curl = curl_easy_init();
  if(curl){
    const char *data = "{\"instanceId\":33,\"token\":\"TOKEN\",\"to\":\"+573001112233\",\"type\":\"text\",\"message\":\"Hola 👋\"}";
    curl_easy_setopt(curl, CURLOPT_URL, "http://<tu-host>:3000/api/send-message");
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
    struct curl_slist *headers = NULL;
    headers = curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_perform(curl);
    curl_easy_cleanup(curl);
  }
  return 0;
}
(require '[clj-http.client :as client])
(client/post "http://<tu-host>:3000/api/send-message"
             {:headers {"Content-Type" "application/json"}
              :body "{\"instanceId\":33,\"token\":\"TOKEN\",\"to\":\"+573001112233\",\"type\":\"text\",\"message\":\"Hola 👋\"}"})
import 'package:http/http.dart' as http;
void main() {
  http.post(Uri.parse('http://<tu-host>:3000/api/send-message'),
    headers:{'Content-Type':'application/json'},
    body:'{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"text","message":"Hola 👋"}');
}
import Foundation
let url = URL(string: "http://<tu-host>:3000/api/send-message")!
var req = URLRequest(url: url)
req.httpMethod = "POST"
req.addValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpBody = "{\"instanceId\":33,\"token\":\"TOKEN\",\"to\":\"+573001112233\",\"type\":\"text\",\"message\":\"Hola 👋\"}".data(using: .utf8)
URLSession.shared.dataTask(with: req).resume()
#import <Foundation/Foundation.h>
int main(){
  NSURL *url = [NSURL URLWithString:@"http://<tu-host>:3000/api/send-message"];
  NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
  req.HTTPMethod = @"POST";
  [req addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
  req.HTTPBody = [@"{\"instanceId\":33,\"token\":\"TOKEN\",\"to\":\"+573001112233\",\"type\":\"text\",\"message\":\"Hola 👋\"}" dataUsingEncoding:NSUTF8StringEncoding];
  [[[NSURLSession sharedSession] dataTaskWithRequest:req] resume];
  return 0;
}
Invoke-RestMethod -Uri http://<tu-host>:3000/api/send-message -Method Post -Body '{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"text","message":"Hola 👋"}' -ContentType 'application/json'
wget --method=POST --header='Content-Type: application/json' \
  --body-data='{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"text","message":"Hola 👋"}' \
  http://<tu-host>:3000/api/send-message
Acepta +E.164, solo dígitos (10–15) o JID (@c.us/@g.us). Si envías dígitos, lo normalizamos a JID.
Autenticación
Token por instancia en el body JSON
  • Éxito: {"success":true, ...}
  • 401: token inválido o instancia no coincide
  • 409: instancia aún no está CONNECTED
POST
/api/send-message
Un endpoint, todos los tipos
text
image
video
audio
document
sticker
location
contact
buttons
list

Texto

-d '{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"text","message":"Hola 👋"}'

Imagen (URL) con caption

-d '{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"image","media":"https://picsum.photos/600/400","caption":"Demo"}'

Video (URL)

-d '{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"video","media":"https://dominio.com/video.mp4"}'

Audio (nota de voz)

-d '{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"audio","media":"https://dominio.com/audio.mp3"}'

Documento (URL) + nombre

-d '{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"document","media":"https://dominio.com/archivo.pdf","filename":"cotizacion.pdf"}'

Sticker (URL)

-d '{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"sticker","media":"https://dominio.com/sticker.png"}'

Ubicación

-d '{"instanceId":33,"token":"TOKEN","to":"+573001112233","type":"location","latitude":6.25184,"longitude":-75.56359}'

Contacto (vCard)

-d '{
  "instanceId":33,
  "token":"TOKEN",
  "to":"+573001112233",
  "type":"contact",
  "contact":"BEGIN:VCARD\nVERSION:3.0\nFN:Soporte Whapi\nTEL;TYPE=CELL:+573001112233\nEND:VCARD"
}'

Botones

-d '{
  "instanceId":33,
  "token":"TOKEN",
  "to":"+573001112233",
  "type":"buttons",
  "message":"Elige una opción",
  "buttons":[{"id":"comprar","text":"Comprar"},{"id":"hablar","text":"Hablar con agente"}]
}'

Lista

-d '{
  "instanceId":33,
  "token":"TOKEN",
  "to":"+573001112233",
  "type":"list",
  "list":{
    "title":"Catálogo",
    "buttonText":"Ver opciones",
    "sections":[
      {"title":"Zapatos","rows":[{"id":"z1","title":"Deportivos"},{"id":"z2","title":"Casuales"}]},
      {"title":"Accesorios","rows":[{"id":"a1","title":"Gorras"}]}
    ]
  }
}'
Respuestas: 200 {"success":true,...}, 409 si la instancia aún no está CONNECTED, 401 token inválido, 400 validación, 500 error interno.
POST
/api/upload-media
Sube tu archivo y usa la URL devuelta en send-message
curl -X POST http://<tu-host>:3000/api/upload-media \
  -F instanceId=33 -F token=TOKEN -F file=@/ruta/a/archivo.png
Respuesta: {"success":true, "media_url":"https://.../uploads/whatsapp/<archivo>"}
Eventos Webhook
Te notificamos en tiempo real

Configura webhook_url por instancia. Enviamos POST JSON con estos eventos clave:

  • message_received — mensajes entrantes (incluye medios cuando es posible)
  • message_sent — mensajes salientes (desde tu instancia)
  • ack_update — estado: sent|delivered|read
  • message_reaction — reacciones (👍 ❤️)
Probar webhook
curl -X POST http://<tu-host>:3000/api/test-webhook \
  -H 'Content-Type: application/json' \
  -d '{"instanceId":33,"token":"TOKEN"}'
Formato tipo (ej.)
{
  "event_type": "message_received",
  "id": "ABCD...",
  "instance_id": 33,
  "chat_id": "57300...@c.us",
  "from": "57300...@c.us",
  "to": "57300...@c.us",
  "from_me": 0,
  "sender_name": "Carlos",
  "body": "Hola",
  "type": "text",
  "timestamp": 1756196000,
  "is_group": 0,
  "media_url": null,
  "status": "received",
  "ack": "pending"
}
Instancias
Crea, reinicia y obtén QR
Estado API
GET
/api/status
curl http://<tu-host>:3000/api/status
QR de conexión
GET
/api/instance-qr/{instanceId}
curl http://<tu-host>:3000/api/instance-qr/33
Crear instancia (si existe en tu BD)
POST
/api/create-instance
-d '{"instanceId":"33"}'
Eliminar instancia
POST
/api/delete-instance
-d '{"instanceId":"33"}'
Reiniciar instancia
POST
/api/restart-instance
-d '{"instanceId":"33"}'
Inbox & datos
Mensajes, contactos y grupos
Mensajes (paginado)
GET
/api/messages/{instanceId}?limit=100&offset=0
curl "http://<tu-host>:3000/api/messages/33?limit=100&offset=0"
Mensajes por chat
GET
/api/messages/{instanceId}/{chatId}
curl "http://<tu-host>:3000/api/messages/33/573001112233@c.us?limit=50"
Contactos
GET
/api/contacts/{instanceId}
curl http://<tu-host>:3000/api/contacts/33
Grupos
GET
/api/groups/{instanceId}
curl http://<tu-host>:3000/api/groups/33
Gestión de grupos
Crea y administra miembros
Crear grupo
POST
/api/create-group
-d '{
  "instanceId":"33",
  "token":"TOKEN",
  "groupName":"Clientes VIP",
  "participants":["+573001112233","+523311112233"]
}'
Administrar grupo
POST
/api/manage-group
-d '{
  "instanceId":"33",
  "token":"TOKEN",
  "groupId":"573001112233-123456@g.us",
  "action":"add_participant",
  "data": {"participant":"+523311112233"}
}'
Extras (Addons)
Funciones opcionales
  • GET /api/health — estado de salud básico
  • GET /api/metrics — métricas estilo Prometheus
  • GET /api/instances — lista de instancias activas
  • GET /api/instance/<id>/state — estado actual de la instancia
  • POST /api/instance/<id>/webhook — actualiza webhook_url
  • POST /api/resolve-number — valida si el número usa WhatsApp
  • GET /api/events/stream — stream SSE de eventos
  • POST /api/simulate-incoming — simula un message_received
  • POST /api/bulk — envíos masivos
  • GET /api/bulk/<jobId> — estado de job
  • POST /api/bulk/<jobId>/pause, resume, cancel
  • GET /api/webhook-dlq — cola de eventos fallidos
  • POST /api/webhook-dlq/<id>/replay — reintenta evento
GET
/api/stats/{instanceId}
Conteos rápidos por estado (filtros por fecha)
curl "http://<tu-host>:3000/api/stats/33?start_date=2025-08-01T00:00:00Z&end_date=2025-08-26T23:59:59Z"
Respuesta tipo: {"messages_sent":123,"messages_received":45,"messages_delivered":110,"messages_read":98}
Estados y límites
Integración predecible
Códigos HTTP
  • 200 Éxito
  • 400 Validación
  • 401 Token inválido
  • 404 No encontrado
  • 409 Instancia no CONNECTED
  • 429 Rate limit (100 req/min por token/IP)
  • 500 Error interno (logeado en la tabla errors)
Buenas prácticas
  • Usa +E.164 o JID (@c.us/@g.us)
  • Si ves sent (partial), la confirmación exacta llega por webhook
  • Maneja reintentos idempotentes en tu webhook
© Whapi.co — Simple. Estable. Escalable.