Versión 2026-07-31 · Autenticación Bearer por fuente · Idempotente por external_id
Cuando tu flujo (Respond.io u otro emisor HTTP) capture una respuesta de encuesta, un POST a este webhook la guarda en la tabla de encuestas del workspace, lista para análisis y dashboards.
svik_…) —
un administrador Frepi los emite desde el panel de administración
(Fuentes → «Emitir secreto NPS»). El secreto se muestra una sola
vez y se comparte por canal seguro — nunca dentro de documentos.$nps_score y
$nps_comment — sustitúyelas por los nombres reales de TU flujo.| Campo | Valor |
|---|---|
| Método | POST |
| URL | https://{dominio-del-workspace}/api/webhooks/survey/{routing_id} (la URL exacta viene en el diálogo de emisión del secreto) |
| Header 1 | Authorization: Bearer svik_… |
| Header 2 | Content-Type: application/json |
Body (JSON — ejemplo con variables de Respond.io):
{
"external_id": "$contact.id-$workflow.execution_id",
"survey_type": "nps",
"contact_id": "$contact.phone",
"response_value": "$nps_score",
"response_text": "$nps_comment",
"question": "¿Qué tan probable es que nos recomiendes?",
"channel": "whatsapp",
"topic": "servicio_pqrs",
"metadata": {
"fcr": true,
"empatia": true
},
"responded_at": "$system.timestamp"
}
| Campo | Regla |
|---|---|
external_id | Obligatorio y único por respuesta. Defensa anti-duplicados: un reintento con el mismo external_id es un no-op (outcome: duplicate). Contacto + id de ejecución del flujo es la combinación natural. |
survey_type | nps (entero 0–10) o csat (1–5). |
contact_id | Teléfono del contacto. Se normaliza a solo dígitos. |
response_value | Nota numérica. NPS fuera de 0–10 o no-entero se guarda sin clasificar (promotor/pasivo/detractor queda NULL). |
response_text | Comentario libre. Opcional. |
question | Texto de la pregunta tal como se formuló. Opcional. |
channel | Opcional. Cómo se recolectó la respuesta (no el sistema de origen). Minúsculas, ≤ 32 caracteres. Vocabulario documentado: whatsapp, phone, webchat; otros valores se aceptan y se guardan. Ausente → NULL (no se asume WhatsApp). |
topic | Opcional. Tema / motivo de la encuesta, ≤ 64 caracteres (ej. servicio_pqrs). |
metadata | Opcional. Bolsa plana de sub-respuestas tipadas (ver §3.1). No uses este campo para cédula ni para campos que Frepi ya modela (channel, topic, score). |
responded_at | ISO-8601 o epoch (segundos o milisegundos) — ambos aceptados. |
ON CONFLICT DO NOTHING.
Reenviar un channel o topic corregido con el mismo
external_id responde {"outcome": "duplicate"} con HTTP
200 y no cambia nada. Ese 200 no significa que la corrección
se aplicó.metadata — reglas y política de violación| Regla | Valor |
|---|---|
| Forma | Objeto JSON plano (sin anidar) |
| Tipos de valor | string · number · boolean. Claves con valor null se descartan en silencio. |
| Máx. claves | 16 |
| Tamaño serializado | ≤ 2048 bytes UTF-8 |
| Patrón de clave | ^[a-z][a-z0-9_]{0,39}$ |
| Longitud de string | ≤ 256 caracteres por valor |
Dos clases de violación (híbrida):
metadata no es un objeto, o
algún valor es a su vez un objeto o un array. El contrato del emisor está
mal y debe corregirse.channel,
topic); solo se pierde metadata. No se
conservan claves a medias.Sub-respuestas sí/no (FCR, Empatía, …) viajan como booleanos
JSON (true/false), no como "Sí"/"No".
| Código | Significado | Qué hacer |
|---|---|---|
200 | outcome: inserted (guardada) o outcome: duplicate (ya existía). | Nada — ambos son éxito. Recuerda: duplicate no aplica correcciones (write-once). |
401 | Secreto incorrecto o ausente. | Revisar el header; rotar el secreto si se perdió. |
403 | La fuente no tiene secreto activo (revocado o nunca emitido). | Emitir uno desde el panel de administración. |
404 | routing_id desconocido. | Verificar la URL emitida. |
422 | Body inválido (falta external_id, nota no numérica, metadata estructuralmente inválido…). | Revisar el mapeo de variables. |
503 | Base de datos no alcanzada a tiempo. | Reintentar es seguro — external_id evita duplicados. |
Presupuesto de tiempo: respondemos en ~6 s o devolvemos 503 (Respond.io corta pasos HTTP a los 10 s; el reintento es seguro).
9 y un comentario
reconocible (ej. "prueba humo NPS").200 con "outcome": "inserted"."outcome": "duplicate"
y en Frepi sigue habiendo UNA sola fila.