Ir al contenido
Nigora Zap API

    Entrar Crear cuenta

    Webhooks

    Los webhooks son llamadas POST que la API hace a una dirección de su sistema cuando algo ocurre: llegó un mensaje, se envió un mensaje, el número se conectó o se desconectó.

    Evento Cuándo ocurre
    message.received Llegó un mensaje
    message.sent Se envió un mensaje de la cola
    message.status Cambió la situación de un mensaje (incluye fallos de envío)
    presence Cambio de presencia de un contacto, como “escribiendo”. El cuerpo trae chatId y presence
    instance.connected El número se conectó. El cuerpo trae phone
    instance.disconnected El número se desconectó. El cuerpo trae reason y loggedOut
    contact.opted_out Un contacto pidió no recibir más mensajes. El cuerpo trae phone

    PUT /v1/instances/{instanceId}/webhooks reemplaza toda la configuración. Envíe siempre el conjunto completo. Cada evento tiene su propia URL y puede activarse o desactivarse:

    Ventana de terminal
    curl -X PUT https://api.zap.nigora.com.br/v1/instances/INSTANCIA_ID/webhooks \
    -H "Authorization: Bearer TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
    "events": {
    "message.received": { "url": "https://meusistema.com.br/hooks/recebida", "enabled": true },
    "message.status": { "url": "https://meusistema.com.br/hooks/status", "enabled": true }
    },
    "notifySentByMe": false,
    "receiveFilters": {
    "ignoreGroups": true
    }
    }'

    La respuesta (200) trae la configuración guardada y el secret, el secreto que se usa en la firma.

    Las URL pueden tener hasta 2.048 caracteres y deben ser públicas. Las direcciones de la red interna y de loopback (como localhost) se rechazan con 400 y url_not_allowed. Los campos que no sigan el formato anterior también devuelven 400 (validation_error). Los eventos sin URL, o con enabled: false, no generan llamadas.

    En receiveFilters, cada campo es false de forma predeterminada. Actívelo para no recibir ese tipo de mensaje:

    ignoreGroups, ignorePrivate, ignoreText, ignoreImage, ignoreVideo, ignoreAudio, ignoreDocument.

    Con notifySentByMe activado, usted también recibe message.received para los mensajes que usted mismo envió desde el celular.

    Para leer la configuración actual, use GET /webhooks. La respuesta incluye el secreto de la firma.

    El cuerpo es siempre JSON, con este formato:

    {
    "event": "message.received",
    "instanceId": "…",
    "timestamp": "2026-11-02T14:30:00.000Z",
    "data": { }
    }

    data cambia según el evento. En message.received:

    Campo Descripción
    waMessageId Identificador del mensaje en WhatsApp
    chatId Conversación, como 5511999999999@s.whatsapp.net (los grupos terminan en @g.us)
    from Quién lo envió, en el mismo formato
    fromMe true si lo envió el propio número
    isGroup true si vino de un grupo
    timestamp Hora del mensaje, en segundos desde 1970
    type text, image, video, audio, document u other
    text Texto, o null
    quotedMessageId Mensaje respondido, o null
    pushName Nombre visible de quien lo envió, o null
    media Para multimedia: url, mimeType, fileName y expiresAt
    mediaSkipped true si el archivo no pudo descargarse o era demasiado grande

    En message.sent, data trae id (el devuelto en el 202 del envío), waMessageId y type. En message.status, trae id, waMessageId y status (sent, delivered, read o failed); cuando el envío falla, viene también error (vea Envío de mensajes).

    Encabezado Contenido
    x-nigora-event Nombre del evento
    x-nigora-delivery Identificador de esta entrega
    x-nigora-signature Firma del cuerpo, con el formato sha256=<hex>

    Antes de confiar en una llamada, compruebe la firma. Es el HMAC-SHA256 de los bytes exactos del cuerpo recibido, calculado con el secreto de la instancia (el secret de GET /webhooks) y escrito en hexadecimal, después del prefijo sha256=.

    import { createHmac, timingSafeEqual } from 'node:crypto'
    // `corpoBruto` es el cuerpo recibido, sin pasar por JSON.parse ni volver a serializarse.
    function assinaturaValida(corpoBruto, cabecalho, segredo) {
    const esperada = 'sha256=' + createHmac('sha256', segredo).update(corpoBruto).digest('hex')
    const a = Buffer.from(esperada)
    const b = Buffer.from(cabecalho ?? '')
    return a.length === b.length && timingSafeEqual(a, b)
    }
    <?php
    $corpo = file_get_contents('php://input');
    $esperada = 'sha256=' . hash_hmac('sha256', $corpo, $segredo);
    if (!hash_equals($esperada, $_SERVER['HTTP_X_NIGORA_SIGNATURE'] ?? '')) {
    http_response_code(401);
    exit;
    }

    Para cambiar el secreto, llame a POST /webhooks/secret/regenerate. La respuesta es { "secret": "..." } y el anterior deja de valer. Los intentos y reenvíos hechos después del cambio ya salen firmados con el secreto nuevo.

    Responda con cualquier estado 2xx para confirmar, en un máximo de 10 segundos. Cualquier otra respuesta, o la falta de respuesta, cuenta como fallo y la API vuelve a intentarlo, con una espera creciente entre intentos (la primera espera es de 5 segundos y se duplica con cada fallo), hasta 8 intentos en total. Después de eso, la entrega queda como failed.

    Haga el procesamiento pesado después de responder. Confirme primero y trabaje a continuación, de preferencia en una cola propia.

    Como puede haber reenvíos, use el encabezado x-nigora-delivery para ignorar una entrega que usted ya procesó.

    La API guarda las entregas de los últimos 7 días.

    Operación Ruta
    Listar entregas, de la más reciente a la más antigua GET /webhooks/deliveries (parámetros limit, de 1 a 100, y before, una fecha ISO 8601 con zona horaria, para paginar)
    Ver una entrega: evento, situación, intentos y contenido GET /webhooks/deliveries/{id}
    Reenviar una entrega POST /webhooks/deliveries/{id}/resend

    La lista viene en { "deliveries": [...] }. Cada entrega trae id, event, url, status (pending, success o failed), attempts, lastStatusCode, lastResponse, createdAt y updatedAt. La consulta de una entrega agrega el payload.

    El reenvío responde 202 con { "id": "..." }, el identificador de una nueva entrega con el mismo contenido, a la misma URL. Un id que no existe en esta instancia devuelve 404 con webhook_delivery_not_found.

    El historial es útil cuando su sistema estuvo fuera de servicio: liste las entregas con fallo y reenvíelas.

    Active la baja en PATCH /settings:

    Ventana de terminal
    curl -X PATCH https://api.zap.nigora.com.br/v1/instances/INSTANCIA_ID/settings \
    -H "Authorization: Bearer TOKEN" \
    -H "Content-Type: application/json" \
    -d '{ "optOut": { "enabled": true, "keywords": ["SAIR", "PARAR"] } }'

    Cuando un contacto envía, en una conversación privada, un mensaje que sea solo una de esas palabras (sin distinguir mayúsculas, minúsculas ni acentos), entra en la lista de bajas y usted recibe el evento contact.opted_out. Los nuevos envíos a ese contacto se rechazan con 422 (contact_opted_out). La palabra predeterminada es SAIR.

    • GET /opt-outs lista los contactos dados de baja (con limit y before para paginar).
    • DELETE /opt-outs/{phone} permite volver a enviar a un teléfono. Responde 204, aunque el teléfono no estuviera en la lista.