Encuestas
Consultar encuestas, respuestas, estadisticas y encuestas no enviadas
Con estos endpoints puedes leer los resultados de las encuestas de tu organizacion (CSAT, NPS, si/no, opcion unica y texto libre) para llevarlos a tu propio sistema, BI o reportes.
Todos son GET y de solo lectura. La URL base y la autenticacion son las mismas que el resto de la CRM API.
Endpoints
| Metodo | Ruta | Descripcion |
|---|---|---|
GET | /crm/organizations/{organization_id}/surveys | Listar encuestas |
GET | /crm/organizations/{organization_id}/surveys/:id | Obtener una encuesta con sus preguntas |
GET | /crm/organizations/{organization_id}/surveys/:id/responses | Listar respuestas (paginado, JSON) |
GET | /crm/organizations/{organization_id}/surveys/:id/responses/export | Exportar respuestas (CSV) |
GET | /crm/organizations/{organization_id}/surveys/:id/stats | Estadisticas de una encuesta |
GET | /crm/organizations/{organization_id}/surveys/stats/by-agent | Resultados por asesor |
GET | /crm/organizations/{organization_id}/surveys/:id/skips | Encuestas que no se enviaron y por que |
GET | /crm/organizations/{organization_id}/surveys/responses/by-contact/:contact_id | Respuestas de un contacto (todas las encuestas) |
Listar encuestas
GET /crm/organizations/{organization_id}/surveysDevuelve todas las encuestas de la organizacion (incluidas las pausadas y archivadas), de la mas nueva a la mas vieja, con un resumen de resultados. Las encuestas eliminadas no aparecen.
Query Parameters
| Parametro | Tipo | Descripcion |
|---|---|---|
channel | string | whatsapp, email, sms, voice |
Ejemplo
curl "https://api.mirlo.com/v1/crm/organizations/{organization_id}/surveys" \
-H "X-API-Key: <YOUR_API_KEY>"Respuesta
Un arreglo (sin paginacion):
[
{
"id": "0f3c1a52-6d2e-4b8a-9a51-2b7e4c9d1f10",
"name": "Satisfaccion post-atencion",
"type": "csat",
"status": "active",
"channel": "whatsapp",
"trigger_kind": "conversation_archived",
"definition": { "questions": [ ... ] },
"created_at": "2026-08-01T15:00:00.000Z",
"updated_at": "2026-09-12T18:20:00.000Z",
"_count": { "responses": 1342 },
"score_scale": { "max": 5, "good_from": 4, "bad_up_to": 2 },
"response_rate": 0.41,
"avg_score": 4.6,
"nps": null
}
]| Campo | Descripcion |
|---|---|
type | csat, nps, yes_no, single_choice, open_text |
status | draft, active, paused, archived |
trigger_kind | Que dispara el envio: conversation_archived (al cerrar la conversacion), campaign o flow |
response_rate | Fraccion de 0 a 1: respondidas / enviadas |
avg_score | Promedio del puntaje principal. null si no hay respuestas con puntaje |
score_scale | Escala del puntaje principal (max 5 o 10) y los cortes de "bueno" y "malo". null si la encuesta no tiene pregunta con puntaje |
nps | Solo en encuestas nps: { promoters, passives, detractors, score } |
Obtener una encuesta
GET /crm/organizations/{organization_id}/surveys/:idDevuelve la encuesta con su definition: la lista de preguntas. La necesitas para interpretar answers en las respuestas.
Estructura de definition
{
"questions": [
{ "key": "q1", "text": "Como calificarias la atencion?", "type": "scale", "scaleMax": 5 },
{
"key": "q2",
"text": "Que tenemos que mejorar?",
"type": "single_choice",
"options": [
{ "id": "precios", "title": "Mejores precios" },
{ "id": "otros", "title": "Otra cosa", "allowFreeText": true }
]
},
{ "key": "q3", "text": "Volverias a comprar?", "type": "yes_no" }
],
"comment_prompt": "Gracias! Quieres dejarnos un comentario?"
}| Tipo de pregunta | Valor guardado en answers |
|---|---|
scale | Numero de 1 a 5 (o de 1 a 10 si scaleMax es 10), como texto: "4" |
nps | Numero de 0 a 10, como texto |
yes_no | Lo que respondio el contacto, normalmente "yes"/"no" o "Si"/"No" |
single_choice | El id de la opcion elegida: "precios" |
open_text | El texto que escribio el contacto |
answers guarda la respuesta tal como llego. Cuando el contacto toca un boton o una lista, es el id de la opcion; pero si escribe en lugar de tocar, puede ser texto libre (por ejemplo "un 8 diria" en una NPS). Si una opcion tiene allowFreeText, el texto adicional llega en la clave <key>_other (por ejemplo q2_other).
Si prefieres las respuestas ya interpretadas, con el texto de la opcion en lugar del id, usa el export CSV.
Listar respuestas
GET /crm/organizations/{organization_id}/surveys/:id/responsesUna fila por cada envio de la encuesta a un contacto, de la mas reciente a la mas antigua (por fecha de envio).
Query Parameters
| Parametro | Tipo | Descripcion |
|---|---|---|
status | string | Uno o varios separados por coma: sent, partial, completed, expired, failed, cancelled |
start | string | Fecha ISO 8601. Envios con sent_at mayor o igual |
end | string | Fecha ISO 8601. Envios con sent_at menor o igual |
score_min | number | Puntaje minimo (0 a 10) |
score_max | number | Puntaje maximo (0 a 10) |
q | string | Busca en el telefono/correo del contacto, su nombre o la referencia (source_ref) |
metadata_key | string | Filtra por un dato del disparador. Va junto con metadata_value |
metadata_value | string | Valor de metadata_key |
limit | number | Resultados por pagina. Default 50, maximo 200 |
offset | number | Offset para paginacion |
start y end filtran por fecha de envio (sent_at), no por fecha de respuesta. Una fecha sin hora (2026-10-05) se toma como las 00:00 UTC de ese dia, asi que end=2026-10-05 deja afuera el dia 5. Para rangos exactos manda la fecha y hora con zona horaria, por ejemplo end=2026-10-05T23:59:59-06:00.
Ejemplo
curl "https://api.mirlo.com/v1/crm/organizations/{organization_id}/surveys/{survey_id}/responses?status=completed&start=2026-10-01T00:00:00-06:00&limit=100" \
-H "X-API-Key: <YOUR_API_KEY>"Respuesta
{
"items": [
{
"id": "a4b1f2c3-1111-4e5f-8a9b-0c1d2e3f4a5b",
"survey_id": "0f3c1a52-6d2e-4b8a-9a51-2b7e4c9d1f10",
"status": "completed",
"score": 5,
"answers": { "q1": "5", "q2": "precios", "q3": "yes" },
"comment": "Muy rapida la atencion",
"contact_id": "contact_c1d2e3f4-2222-4a5b-9c8d-7e6f5a4b3c2d",
"contact_address": "5215512345678",
"contact_name": "Ana Lopez",
"conversation_id": "8f7e6d5c-4444-4b3a-9c2d-1e0f9a8b7c6d",
"organization_address": "5215598765432",
"agent_member_id": "9a8b7c6d-3333-4e2f-8a1b-5c4d3e2f1a0b",
"source": "conversation_archived",
"source_ref": null,
"metadata": {},
"sent_at": "2026-10-03T16:02:11.000Z",
"responded_at": "2026-10-03T16:05:40.000Z",
"completed_at": "2026-10-03T16:07:02.000Z",
"expires_at": "2026-10-04T00:02:11.000Z",
"created_at": "2026-10-03T16:02:11.000Z",
"updated_at": "2026-10-03T16:07:02.000Z"
}
],
"total": 318,
"limit": 100,
"offset": 0
}A diferencia de otros listados de la CRM API, este devuelve las filas en items (no en data).
| Campo | Descripcion |
|---|---|
status | sent (enviada, sin respuesta todavia), partial (contesto algunas preguntas), completed, expired (vencio sin terminar), failed (no se pudo entregar), cancelled (se corto, por ejemplo porque un asesor intervino en la conversacion) |
score | Puntaje principal: el de la primera pregunta con puntaje (escala, NPS o si/no). null si no hay |
answers | Respuesta por pregunta, con la key de la pregunta como clave. Ver estructura de definition |
comment | Comentario final del contacto, si lo dejo |
contact_address | Telefono (o correo) al que se envio |
contact_name | Nombre del contacto en el CRM. null si no se pudo vincular |
agent_member_id | ID del asesor al que se atribuye la respuesta. null si no aplica |
source | Origen del envio: conversation_archived, campaign, flow, ticket, api |
source_ref | Referencia del disparador (por ejemplo, un numero de orden enviado desde un flujo) |
metadata | Datos adicionales del disparador (por ejemplo, la sucursal) |
Sincronizar respuestas periodicamente
Para traer respuestas a tu sistema de forma incremental:
- Pide
status=completedconstart/endacotando una ventana de fecha de envio. - Pagina con
offsethasta queoffset + limit >= total. - Usa
idcomo clave para no duplicar filas.
Como el filtro es por fecha de envio, una encuesta enviada al final de una ventana puede completarse despues de que la consultaste. Para no perder esas respuestas, vuelve a consultar una ventana que se superponga con la anterior (por ejemplo, las ultimas 48 horas, o el plazo de vencimiento de la encuesta) y deduplica por id.
Exportar respuestas (CSV)
GET /crm/organizations/{organization_id}/surveys/:id/responses/exportDevuelve todas las respuestas que cumplen los filtros, sin paginacion, en un archivo CSV (UTF-8 con BOM, compatible con Excel). Acepta los mismos filtros que el listado, salvo limit y offset: status, start, end, score_min, score_max, q, metadata_key y metadata_value.
curl "https://api.mirlo.com/v1/crm/organizations/{organization_id}/surveys/{survey_id}/responses/export?status=completed" \
-H "X-API-Key: <YOUR_API_KEY>" \
-o respuestas.csvColumnas: Contacto, Nombre del contacto, Referencia, Asesor (nombre), Origen, Estado, Enviada, Respondida, Completada, una columna por cada clave de metadata, una columna por pregunta (con la respuesta ya interpretada, por ejemplo el texto de la opcion elegida) y Comentario.
Las fechas del CSV estan en hora de la Ciudad de Mexico, con formato dd/mm/aaaa hh:mm.
Estadisticas de una encuesta
GET /crm/organizations/{organization_id}/surveys/:id/statsQuery Parameters
| Parametro | Tipo | Descripcion |
|---|---|---|
start | string | Fecha ISO 8601 (por fecha de envio) |
end | string | Fecha ISO 8601 (por fecha de envio) |
metadata_key | string | Filtra por un dato del disparador. Va junto con metadata_value |
metadata_value | string | Valor de metadata_key |
curl "https://api.mirlo.com/v1/crm/organizations/{organization_id}/surveys/{survey_id}/stats?start=2026-09-01T00:00:00-06:00&end=2026-09-30T23:59:59-06:00" \
-H "X-API-Key: <YOUR_API_KEY>"Campos principales de la respuesta:
| Campo | Descripcion |
|---|---|
sent, responded, completed, expired, cancelled, failed | Conteos del periodo |
skipped, skipped_by_reason | Encuestas que no se enviaron, total y por motivo |
response_rate | Fraccion de 0 a 1 |
avg_score, score_scale, nps | Puntaje promedio, su escala y el NPS (solo encuestas nps) |
distribution | Cantidad de respuestas por puntaje (o por opcion, en encuestas single_choice) |
option_labels | En encuestas single_choice: id de opcion → texto |
questions | Resultados por pregunta |
by_source, by_agent | Cortes por origen y por asesor |
timeseries | Conteos por dia (date en UTC) |
recent_comments | Los ultimos 20 comentarios |
text_answers | En encuestas open_text: las ultimas 100 respuestas de texto |
Resultados por asesor
GET /crm/organizations/{organization_id}/surveys/stats/by-agentAgrupa las respuestas por el asesor al que se atribuyen, combinando varias encuestas del mismo tipo.
Query Parameters
| Parametro | Tipo | Descripcion |
|---|---|---|
survey_ids | string | IDs de encuesta separados por coma. Sin este parametro se usan todas |
type | string | csat, nps, yes_no, single_choice, open_text |
start | string | Fecha ISO 8601 (por fecha de envio) |
end | string | Fecha ISO 8601 (por fecha de envio) |
min_responses | number | Oculta asesores con menos respuestas que este numero |
group_by_metadata | string | Agrega un corte por esa clave de metadata |
Si pasas survey_ids de tipos o escalas distintas (por ejemplo, una CSAT de 1 a 5 y una NPS de 0 a 10), la respuesta es 400: sus puntajes no se pueden promediar entre si.
curl "https://api.mirlo.com/v1/crm/organizations/{organization_id}/surveys/stats/by-agent?type=csat&start=2026-09-01T00:00:00-06:00" \
-H "X-API-Key: <YOUR_API_KEY>"Respuesta (resumida: totals, cada elemento de agents y unattributed traen los mismos conteos completos, incluidos scored, expired y cancelled):
{
"type": "csat",
"score_scale": { "max": 5, "good_from": 4, "bad_up_to": 2 },
"surveys": [ { "id": "0f3c1a52-...", "name": "Satisfaccion post-atencion", "type": "csat" } ],
"period": { "start": "2026-09-01T00:00:00-06:00", "end": null },
"totals": { "sent": 900, "responded": 370, "completed": 350, "expired": 500, "cancelled": 30, "response_rate": 41.1, "avg_score": 4.5 },
"agents": [
{ "agent_member_id": "9a8b7c6d-...", "sent": 120, "responded": 52, "completed": 50, "response_rate": 43.3, "avg_score": 4.8 }
],
"teams": [ ... ],
"unattributed": { "sent": 80, "responded": 20, "avg_score": 4.1 }
}Aqui response_rate es un porcentaje (0 a 100). En el listado de encuestas y en stats es una fraccion (0 a 1).
Encuestas no enviadas
GET /crm/organizations/{organization_id}/surveys/:id/skipsEnvios que no salieron, con el motivo. Paginado con limit (default 50, maximo 200) y offset; las filas vienen en items.
| Parametro | Tipo | Descripcion |
|---|---|---|
reason | string | Uno o varios separados por coma (ver tabla) |
start, end | string | Fecha ISO 8601, por fecha en que se descarto (skipped_at) |
q, metadata_key, metadata_value | string | Igual que en respuestas |
limit, offset | number | Paginacion |
reason | Significado |
|---|---|
survey_in_progress | El contacto ya tenia otra encuesta abierta |
cooldown | Ya recibio esta encuesta dentro del periodo minimo entre envios |
window_closed | La ventana de 24 h de WhatsApp estaba cerrada y la encuesta no tiene plantilla |
window_unknown | No se pudo verificar la ventana y la encuesta no tiene plantilla |
no_contact | El disparador no trajo telefono |
no_address | No se pudo resolver el numero de salida |
survey_inactive | La encuesta estaba pausada o archivada |
Tambien existe GET .../surveys/:id/skips/export (CSV, mismos filtros sin paginacion).
Respuestas de un contacto
GET /crm/organizations/{organization_id}/surveys/responses/by-contact/:contact_idLas respuestas de un contacto en todas las encuestas, con status, limit y offset. Cada fila trae survey (id, name, type) y answer_label: la respuesta a la primera pregunta, ya interpretada.
Errores
| Codigo | Cuando |
|---|---|
400 | Parametro invalido (por ejemplo, un status que no existe, limit mayor a 200 o survey_ids de tipos distintos) |
401 | API key invalida |
403 | El organization_id no corresponde a tu API key |
404 | La encuesta no existe o fue eliminada |