SociaHive Docs

Webhooks

Recibe eventos firmados de SociaHive en tu propio endpoint HTTPS y verifícalos.

Un webhook envía un POST HTTPS a tu endpoint cuando ocurre algo en tu cuenta de SociaHive: una publicación programada se publica o falla, una automatización se inicia para alguien o termina con un error, una plantilla de WhatsApp se aprueba o se rechaza.

Agrega uno en el panel, en Integraciones → Webhooks, o con la API REST (/api/v1/webhooks, scopes webhooks:read y webhooks:write).

Cualquiera puede configurar un webhook, pero los eventos solo se entregan mientras tu plan incluya acceso a la API (la prueba gratis y todos los planes de pago). Los webhooks que creaste se conservan y vuelven a enviar eventos cuando cambias a un plan superior.

Límites por plan

PlanWebhooks
Gratis0
Creator2
Studio5
Pro10
Agency50

El límite abarca integration_type webhook, custom y n8n. Las suscripciones de Zapier y Make (zapier, make) no cuentan para él.

Eventos

EventoCuándoCampos de data
post.publishedUna publicación programada salió en una plataformapostId, platform, accountId, platformPostId
post.failedUna publicación programada no se pudo publicar en una plataformapostId, platform, accountId, error
flow.triggeredUna automatización se inició para alguienflowId, executionId, platform, accountId, senderId
flow.errorUna automatización se detuvo con un errorflowId, executionId (cuando se conoce), flowName, errorMessage, errorNodeId
whatsapp.template_statusMeta aprobó, rechazó o pausó una plantilla de WhatsAppaccountId, templateName, templateLanguage, event, reason
post.review_decisionUn cliente aprobó una publicación, pidió cambios o dejó un comentario en un enlace de revisiónpostId, reviewLinkId, kind (approved, changes_requested, comment), reviewerName, comment (cuando existe)
post.plug_firedSe activó un plug en una publicación (una republicación o un comentario)plug_id, post_id, account_id, platform, metric, threshold, value, action

Una publicación que sale en tres plataformas envía tres eventos post.published, uno por plataforma.

post.review_decision requiere enlaces de revisión para clientes (Studio, Pro y Agency) y post.plug_fired requiere plugs (todos los planes de pago). Suscribirte a uno que tu plan no incluye devuelve 400.

El contacto en los eventos sobre una persona

flow.triggered, flow.error (cuando indica a la persona), los eventos de suscriptores del boletín y data.collected también incluyen contact, la persona tal como SociaHive la conoce cuando se envía el evento:

"contact": {
  "id": "6733a1b2c3d4e5f601234567",
  "name": "Jamie Rivera",
  "email": "[email protected]",
  "phone": "+15555550123",
  "status": "active",
  "tags": ["lead", "webinar"],
  "custom_fields": { "Company": "Rivera Studio", "Plan interest": "Pro" },
  "platform_identities": [{ "platform": "instagram", "platform_user_id": "1784140000000000", "username": "jamie.rivera" }]
}

Los campos personalizados usan como clave sus nombres tal como los ves en SociaHive. contact no se envía cuando SociaHive todavía no tiene un contacto para la persona.

Aceptados, aún no se envían

Puedes suscribirte a estos eventos con la API (la app de Make y el nodo de n8n los ofrecen), pero SociaHive todavía no los envía. Un webhook suscrito a ellos los recibirá cuando se lancen, sin que tengas que cambiar nada.

EventoSignificado previsto
message.receivedAlguien envió un mensaje directo a una de tus cuentas
comment.receivedAlguien comentó en una de tus publicaciones
flow.completedUna automatización terminó para alguien
account.disconnectedSe desconectó una cuenta conectada

El panel solo ofrece los eventos que se envían hoy.

Limitar un webhook a algunas cuentas

Define account_ids (ID de cuentas conectadas, de GET /api/v1/connected-accounts) para recibir solo los eventos de esas cuentas. Déjalo vacío para recibir los de todas. Los eventos nombran su cuenta como accountId (post.plug_fired: account_id). Los eventos que no nombran una cuenta (flow.error, post.review_decision) siempre se envían.

Formato de la solicitud

POST /your/endpoint HTTP/1.1
Content-Type: application/json
X-SociaHive-Event: post.published
X-SociaHive-Event-Id: evt_0199a3c4f2b8c1d2e3f4a5b6
X-SociaHive-Timestamp: 1791532800
X-SociaHive-Signature: t=1791532800,v1=5f2b0c9d...
{
  "id": "evt_0199a3c4f2b8c1d2e3f4a5b6",
  "event": "post.published",
  "timestamp": "2026-10-09T12:00:00.000Z",
  "data": {
    "postId": "66f0c2a1b2c3d4e5f6a7b8c9",
    "platform": "instagram",
    "accountId": "66e1a2b3c4d5e6f7a8b9c0d1",
    "platformPostId": "17912345678901234"
  }
}

Responde con cualquier estado 2xx en un máximo de 10 segundos. Haz el trabajo lento después de responder.

Verificar la firma

Cada solicitud se firma con el secreto de firma del webhook, que se muestra una sola vez al crear el webhook (y de nuevo solo cuando generas un secreto nuevo).

X-SociaHive-Signature tiene la forma t=<unix seconds>,v1=<hex>. v1 es el HMAC-SHA256 de la cadena <t>.<raw request body>, con tu secreto como clave.

  1. Lee el cuerpo sin procesar antes de analizar el JSON; volver a serializarlo cambia los bytes.
  2. Calcula el HMAC y compáralo con v1 en tiempo constante.
  3. Rechaza la solicitud si t se aleja más de cinco minutos de la hora actual. Esto impide los ataques de repetición (replay).
import crypto from 'node:crypto'
import express from 'express'
 
const app = express()
const SECRET = process.env.SOCIAHIVE_WEBHOOK_SECRET
const TOLERANCE_SEC = 300
 
function verify(rawBody, header) {
  const parts = Object.fromEntries(
    (header ?? '').split(',').map((p) => p.split('=', 2)),
  )
  const t = Number(parts.t)
  if (!parts.v1 || !Number.isFinite(t)) return false
  if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_SEC) return false
 
  const expected = crypto
    .createHmac('sha256', SECRET)
    .update(`${t}.${rawBody}`)
    .digest('hex')
  const a = Buffer.from(expected, 'hex')
  const b = Buffer.from(parts.v1, 'hex')
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}
 
app.post('/sociahive-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const rawBody = req.body.toString('utf8')
  if (!verify(rawBody, req.get('X-SociaHive-Signature'))) {
    return res.status(400).send('bad signature')
  }
  const event = JSON.parse(rawBody)
  res.sendStatus(200)
  // Handle event.event / event.data here, after answering.
})
 
app.listen(3000)

Entrega y reintentos

  • La entrega es al menos una vez. El mismo evento puede llegar más de una vez; usa id (también en X-SociaHive-Event-Id) para omitir los duplicados.
  • Una entrega fallida (una respuesta distinta de 2xx, un tiempo de espera agotado o la falta de conexión) se intenta hasta 5 veces en total, con esperas cada vez mayores entre intentos.
  • Cuando 3 eventos seguidos fallan en todos sus reintentos, el webhook se desactiva y recibes una notificación en la app. Corrige el endpoint y vuelve a activarlo; el conteo de fallos empieza de nuevo desde cero.
  • No se garantiza que los eventos lleguen en orden. Usa timestamp si el orden importa.
  • Los endpoints deben ser URL https públicas. Las direcciones privadas e internas se rechazan.

Probar un webhook

El botón Enviar prueba del panel envía de inmediato un evento firmado y muestra el código de estado y el tiempo de respuesta:

{
  "id": "evt_...",
  "event": "test",
  "timestamp": "2026-10-09T12:00:00.000Z",
  "data": { "webhook_id": "66f0...", "message": "This is a test event from SociaHive." }
}

Funciona con un webhook desactivado, así que puedes comprobar un endpoint antes de volver a activarlo. Puedes enviar 5 pruebas por minuto.

API REST

MétodoRutaQué hace
GET/api/v1/webhooksLista tus webhooks, incluidos los desactivados
POST/api/v1/webhooksCrea uno: target_url, events, y de forma opcional integration_type (webhook por defecto, custom, n8n, make, zapier) y account_ids. La respuesta incluye signing_secret
PATCH/api/v1/webhooks/{id}Cambia target_url, events, account_ids o active (true lo vuelve a activar y reinicia el conteo de fallos)
DELETE/api/v1/webhooks/{id}Lo elimina. Después puedes agregar de nuevo la misma URL

Cada webhook de una respuesta incluye last_delivery_at, last_status (el estado HTTP, o null cuando el endpoint no respondió) y last_error.

On this page