mirlo

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

MetodoRutaDescripcion
GET/crm/organizations/{organization_id}/surveysListar encuestas
GET/crm/organizations/{organization_id}/surveys/:idObtener una encuesta con sus preguntas
GET/crm/organizations/{organization_id}/surveys/:id/responsesListar respuestas (paginado, JSON)
GET/crm/organizations/{organization_id}/surveys/:id/responses/exportExportar respuestas (CSV)
GET/crm/organizations/{organization_id}/surveys/:id/statsEstadisticas de una encuesta
GET/crm/organizations/{organization_id}/surveys/stats/by-agentResultados por asesor
GET/crm/organizations/{organization_id}/surveys/:id/skipsEncuestas que no se enviaron y por que
GET/crm/organizations/{organization_id}/surveys/responses/by-contact/:contact_idRespuestas de un contacto (todas las encuestas)

Listar encuestas

GET /crm/organizations/{organization_id}/surveys

Devuelve 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

ParametroTipoDescripcion
channelstringwhatsapp, 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
  }
]
CampoDescripcion
typecsat, nps, yes_no, single_choice, open_text
statusdraft, active, paused, archived
trigger_kindQue dispara el envio: conversation_archived (al cerrar la conversacion), campaign o flow
response_rateFraccion de 0 a 1: respondidas / enviadas
avg_scorePromedio del puntaje principal. null si no hay respuestas con puntaje
score_scaleEscala del puntaje principal (max 5 o 10) y los cortes de "bueno" y "malo". null si la encuesta no tiene pregunta con puntaje
npsSolo en encuestas nps: { promoters, passives, detractors, score }

Obtener una encuesta

GET /crm/organizations/{organization_id}/surveys/:id

Devuelve 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 preguntaValor guardado en answers
scaleNumero de 1 a 5 (o de 1 a 10 si scaleMax es 10), como texto: "4"
npsNumero de 0 a 10, como texto
yes_noLo que respondio el contacto, normalmente "yes"/"no" o "Si"/"No"
single_choiceEl id de la opcion elegida: "precios"
open_textEl 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/responses

Una fila por cada envio de la encuesta a un contacto, de la mas reciente a la mas antigua (por fecha de envio).

Query Parameters

ParametroTipoDescripcion
statusstringUno o varios separados por coma: sent, partial, completed, expired, failed, cancelled
startstringFecha ISO 8601. Envios con sent_at mayor o igual
endstringFecha ISO 8601. Envios con sent_at menor o igual
score_minnumberPuntaje minimo (0 a 10)
score_maxnumberPuntaje maximo (0 a 10)
qstringBusca en el telefono/correo del contacto, su nombre o la referencia (source_ref)
metadata_keystringFiltra por un dato del disparador. Va junto con metadata_value
metadata_valuestringValor de metadata_key
limitnumberResultados por pagina. Default 50, maximo 200
offsetnumberOffset 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).

CampoDescripcion
statussent (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)
scorePuntaje principal: el de la primera pregunta con puntaje (escala, NPS o si/no). null si no hay
answersRespuesta por pregunta, con la key de la pregunta como clave. Ver estructura de definition
commentComentario final del contacto, si lo dejo
contact_addressTelefono (o correo) al que se envio
contact_nameNombre del contacto en el CRM. null si no se pudo vincular
agent_member_idID del asesor al que se atribuye la respuesta. null si no aplica
sourceOrigen del envio: conversation_archived, campaign, flow, ticket, api
source_refReferencia del disparador (por ejemplo, un numero de orden enviado desde un flujo)
metadataDatos adicionales del disparador (por ejemplo, la sucursal)

Sincronizar respuestas periodicamente

Para traer respuestas a tu sistema de forma incremental:

  1. Pide status=completed con start/end acotando una ventana de fecha de envio.
  2. Pagina con offset hasta que offset + limit >= total.
  3. Usa id como 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/export

Devuelve 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.csv

Columnas: 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/stats

Query Parameters

ParametroTipoDescripcion
startstringFecha ISO 8601 (por fecha de envio)
endstringFecha ISO 8601 (por fecha de envio)
metadata_keystringFiltra por un dato del disparador. Va junto con metadata_value
metadata_valuestringValor 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:

CampoDescripcion
sent, responded, completed, expired, cancelled, failedConteos del periodo
skipped, skipped_by_reasonEncuestas que no se enviaron, total y por motivo
response_rateFraccion de 0 a 1
avg_score, score_scale, npsPuntaje promedio, su escala y el NPS (solo encuestas nps)
distributionCantidad de respuestas por puntaje (o por opcion, en encuestas single_choice)
option_labelsEn encuestas single_choice: id de opcion → texto
questionsResultados por pregunta
by_source, by_agentCortes por origen y por asesor
timeseriesConteos por dia (date en UTC)
recent_commentsLos ultimos 20 comentarios
text_answersEn encuestas open_text: las ultimas 100 respuestas de texto

Resultados por asesor

GET /crm/organizations/{organization_id}/surveys/stats/by-agent

Agrupa las respuestas por el asesor al que se atribuyen, combinando varias encuestas del mismo tipo.

Query Parameters

ParametroTipoDescripcion
survey_idsstringIDs de encuesta separados por coma. Sin este parametro se usan todas
typestringcsat, nps, yes_no, single_choice, open_text
startstringFecha ISO 8601 (por fecha de envio)
endstringFecha ISO 8601 (por fecha de envio)
min_responsesnumberOculta asesores con menos respuestas que este numero
group_by_metadatastringAgrega 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/skips

Envios que no salieron, con el motivo. Paginado con limit (default 50, maximo 200) y offset; las filas vienen en items.

ParametroTipoDescripcion
reasonstringUno o varios separados por coma (ver tabla)
start, endstringFecha ISO 8601, por fecha en que se descarto (skipped_at)
q, metadata_key, metadata_valuestringIgual que en respuestas
limit, offsetnumberPaginacion
reasonSignificado
survey_in_progressEl contacto ya tenia otra encuesta abierta
cooldownYa recibio esta encuesta dentro del periodo minimo entre envios
window_closedLa ventana de 24 h de WhatsApp estaba cerrada y la encuesta no tiene plantilla
window_unknownNo se pudo verificar la ventana y la encuesta no tiene plantilla
no_contactEl disparador no trajo telefono
no_addressNo se pudo resolver el numero de salida
survey_inactiveLa 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_id

Las 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

CodigoCuando
400Parametro invalido (por ejemplo, un status que no existe, limit mayor a 200 o survey_ids de tipos distintos)
401API key invalida
403El organization_id no corresponde a tu API key
404La encuesta no existe o fue eliminada

En esta pagina