Enviar WhatsApp con Kotlin: ejemplo de API REST (OkHttp y Android)
Para enviar WhatsApp con Kotlin haces un POST con OkHttp a /api/v1/{instanceId}/send/text, con los encabezados x-api-key y x-api-secret y un JSON con number y text. Sirve para backend (Ktor, Spring) y para pruebas. En Android la regla es otra: la app nunca debe guardar la clave y el secreto, porque cualquiera puede extraer cadenas de un APK. La app llama a tu backend, y el backend llama a ZapZap.
Publicado el · 3 min de lectura
Este artículo también está disponible en: Português · English
Qué necesitas antes de empezar
Cuenta en ZapZap, instancia conectada por código QR, clave y secreto de la API y el ID de la instancia. La autenticación usa x-api-key y x-api-secret. Agrega okhttp al build.gradle.kts (com.squareup.okhttp3:okhttp).
Valida la instancia con cURL antes de programar.
cURL
curl -X POST https://api.zapzapapi.com/api/v1/SEU_INSTANCE_ID/send/text \
-H "Content-Type: application/json" \
-H "x-api-key: SUA_API_KEY" \
-H "x-api-secret: SEU_API_SECRET" \
-d '{"number":"5511999999999","text":"Olá! Teste de envio pela API."}'Cómo enviar un mensaje de texto con Kotlin y OkHttp
La clase de abajo crea un único OkHttpClient, con timeouts, y envía el JSON. En Android, las llamadas de red no pueden correr en el hilo principal; aquí el método es suspend y usa Dispatchers.IO.
Para armar JSON con seguridad, usa kotlinx.serialization u org.json en lugar de concatenar texto. El ejemplo usa org.json.JSONObject para evitar errores de escape.
Kotlin
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import java.util.concurrent.TimeUnit
class ZapZapClient(
private val instancia: String,
private val key: String,
private val secret: String,
) {
private val http = OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
private val json = "application/json".toMediaType()
suspend fun enviarTexto(numero: String, texto: String): String = post(
"/send/text",
JSONObject().put("number", numero).put("text", texto),
)
private suspend fun post(caminho: String, corpo: JSONObject): String =
withContext(Dispatchers.IO) {
val req = Request.Builder()
.url("https://api.zapzapapi.com/api/v1/" + instancia + caminho)
.header("x-api-key", key)
.header("x-api-secret", secret)
.post(corpo.toString().toRequestBody(json))
.build()
http.newCall(req).execute().use { r ->
val texto = r.body?.string().orEmpty()
check(r.isSuccessful) { "ZapZap " + r.code + ": " + texto }
texto
}
}
}Cómo enviar imagen, PDF y audio con Kotlin
La ruta es /send/media. El campo type elige el tipo (image, video, audio, ptt, document, sticker y otros), file es la URL pública, text el pie y docName el nombre del documento.
Kotlin
// dentro de ZapZapClient
suspend fun enviarPdf(numero: String, url: String, nome: String, legenda: String = ""): String = post(
"/send/media",
JSONObject()
.put("number", numero)
.put("type", "document")
.put("file", url)
.put("docName", nome)
.put("text", legenda),
)Por qué la app Android debe llamar a tu backend
Todo lo que va dentro de un APK puede extraerse. Si la clave y el secreto de la API están en la app, cualquiera puede enviar mensajes por tu número. El diseño seguro es: la app llama a una ruta tuya, autenticada con el login del usuario, y tu servidor llama a ZapZap con las credenciales guardadas allí.
El backend de abajo, en Ktor, expone POST /avisos y lo reenvía a ZapZap. También es el lugar correcto para aplicar tus reglas: quién puede enviar, límite por usuario y registro del envío.
Kotlin (Ktor)
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.*
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.routing.*
data class Aviso(val numero: String, val texto: String)
fun Application.rotas(zap: ZapZapClient) {
routing {
post("/avisos") {
// aqui: autentique o usuário do app e aplique limites seus
val aviso = call.receive<Aviso>()
val resposta = runCatching { zap.enviarTexto(aviso.numero, aviso.texto) }
resposta.onSuccess { call.respond(HttpStatusCode.Accepted, it) }
.onFailure { call.respond(HttpStatusCode.BadGateway, "falha no envio") }
}
}
}Cómo recibir las respuestas y qué revisar antes de producción
Recibir mensajes es siempre en el servidor: configura la URL en configurar webhook, activa el evento messages y trata data.message (text, chatid, fromMe), ignorando fromMe verdadero. Para avisar a la app, usa push (FCM) desde tu servidor. La firma y la deduplicación están en la guía de webhook en producción.
Lista: credenciales solo en el servidor; timeouts; comprobación de estado; retry solo en 429 y 5xx; límite por usuario; y enviar solo a quien aceptó recibir. Otros lenguajes: Java, Node.js, Python. Todos los endpoints están en la referencia de la API.
Resumen rápido
- OkHttp con corrutinas y Dispatchers.IO resuelve el envío; nunca en el hilo principal.
- La clave y el secreto de la API nunca van dentro de la app: todo lo que está en el APK puede extraerse.
- La app llama a tu backend, y el backend llama a ZapZap.
- Recibir mensajes es en el servidor, por webhook, y la app se avisa por push.
Sigue leyendo
Lee también
Preguntas frecuentes
¿Puedo llamar a la API directo desde la app Android?
Técnicamente sí, pero no debes. La clave y el secreto quedarían dentro del APK y pueden extraerse. Llama a tu backend, que guarda las credenciales.
¿Funciona con Retrofit o Ktor Client?
Sí. La API es REST: los mismos encabezados y el mismo JSON valen en cualquier cliente HTTP.
¿Y Swift o iOS?
Vale la misma regla: la app llama a tu backend. En el servidor sirve cualquier lenguaje de esta serie, y la app solo necesita una ruta tuya.
¿Usar una API no oficial puede llevar al bloqueo del número?
Puede. Una API no oficial va contra los términos de WhatsApp y el número puede ser limitado o bloqueado. Opt-in, ritmo humano, calentamiento y lista limpia reducen el riesgo, pero no hay garantía. Para un canal homologado, usa la API oficial de Meta.
Prueba Kotlin en tu instancia
Crea la cuenta, conecta un número y envía el primer mensaje. R$15 por número activo al mes, sin cobro por mensaje.
