Introducción
¡Bienvenid@ al API de Séntisis Customer eXperience! Aquí encontrarás la documentación necesaria para dar los primeros pasos hacia el análisis programático de tus textos.
Esta API usa graphQL. Para la mayoría de lenguajes existen librerías específicas que facilitan su uso, pero en el fondo no se trata más que de llamadas HTTP al uso con un formato especial en el body. El formato de las respuestas es JSON. GraphQL define unas estructuras de entrada y salida estrictas que conforman el contrato entre cliente y servidor y que llamamos esquema
.
Por ejemplo, una llamada básica con curl
curl --request POST \
--url https://api.cx.sentisis.io/graphql \
--header 'authorization: Bearer <YOUR_TOKEN>' \
--data '{"query":"{\n\tallProjects {\n name\n }\n}"}'
Seguridad
Cabecera de autenticación
Authorization: Bearer YOUR_TOKEN
La API contará con un certificado SSL para proteger cualquier información confidencial que se envía entre los dos sistemas. La autenticación se realizará mediante la llamada Bearer Token Authentication
. Todas las llamadas a la API deberán incluir como cabecera el Bearer Token, que es único por cliente y que puedes encontrar en el Perfil del dashboard de CX.
Mantener en secreto el token de seguridad es responsabilidad del cliente. En el caso de fugas o filtraciones, Séntisis dispone de un método para revocar tokens de acceso.
Errores
Ejemplo de error
{
"errors": [
{
"message": "Dataset not found: fake_dataset_id",
"locations": [
{
"line": 5,
"column": 5
}
],
"path": [
"project",
"dataset"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR"
}
}
],
# Datos parciales
"data": {
"project": {
"id": "project_id",
"name": "Proyecto válido",
"dataset": null
}
}
}
Independientemente del código de status HTTP con el que se resuelvan, las operaciones graphQL siempre devuelven un cuerpo JSON. En caso de error, la respuesta contendrá un atributo nuevo, errors
, con el listado de errores. Es importante tener en cuenta los siguientes puntos:
- Podemos obtener varios errores para una misma petición, aunque el atributo
errors
siempre será una lista - El código de error estará en
extensions.code
- Un error no invalida toda la petición, solo la porción a la que afecta; graphQL intenta devolver tantos metadatos como le sea posible a pesar de los posibles fallos que encuentre. Por ejemplo, para una petición de proyecto y dataset en la que el identificador del dataset es erróneo, los datos del proyecto seguirán siendo accesibles en la estructura
data
La API de Séntisis usa los códigos de error predefinidos de graphQL, que se pueden consultar en el siguiente enlace: https://www.apollographql.com/docs/apollo-server/data/errors/
Para la mayoría de errores la API responderá con un HTTP 200 pero a veces lo hará con un 400 (si la estructura de la petición no se ajusta al esquema) o un 500 (si algo ha ido realmente mal). En todo caso, como hemos dicho, lo importante será revisar el código de error en el body de la respuesta.
Una lista no exhaustiva con algunos ejemplos de errores:
code | message | HTTP code |
---|---|---|
GRAPHQL_VALIDATION_FAILED | Cannot query field "allProject" on type "Query". Did you mean "allProjects" or "project"? | 400 |
GRAPHQL_VALIDATION_FAILED | Expected type Int, found string | 400 |
UNAUTHENTICATED | Authentication Error | 400 |
QUOTA_ERROR | Quota reached | 200 |
INTERNAL_SERVER_ERROR | Dataset not found | 200 |
... | ... | ... |
Arquitectura
La aplicación contará con Proyectos, los cuales contendrán internamente la información sobre cómo se realizará la clasificación de los mensajes (el modelo lingüístico). Por ejemplo, se crearán los proyectos Encuestas, Reclamos y Solicitudes... Dichos proyectos contarán con identificador y un nombre.
Cada proyecto contará con ninguno, uno o varios Datasets. Dichos datasets tendrán un campo identificador, así como campos para el nombre, fecha de creación y estado. El estado indicará si el dataset subido está en el proceso de análisis o listo para ser descargado.
Proyectos
Los proyectos representan modelos de análisis lingüísticos; contienen el conjunto de reglas que se aplicarán a los mensajes recibidos.
Para recibir un listado de los proyectos haremos la petición:
{
allProjects(first: 10 after: 0) {
id
name
createdAt
}
}
Petición
Parámetro | Tipo | Obligatorio | Valor por defecto | Descripción |
---|---|---|---|---|
first | Int | no | 10 | Cantidad de elementos a devolver |
after | Int | no | 0 | Saltándose este número de elementos |
Respuesta
{
"data": {
"allProjects": [
{
"id": "project_id",
"name": "Project Name",
"createdAt": "2020-01-05T11:42:04.314Z"
},
…
]
}
}
Atributo | Tipo | Descripción |
---|---|---|
id | ID! | Id del proyecto |
name | String! | Nombre del proyecto |
createdAt | DateTime! | Fecha de creación |
{
project(id: "project_id") {
id
name
createdAt
}
}
{
"data": {
"project": {
"id": "project_id",
"name": "Project Name",
"createdAt": "2020-01-05T11:42:04.314Z"
}
}
}
Y para ver los datos de un proyecto en concreto, filtraremos por el id del proyecto usando project
en lugar de allProjects
Datasets
Un dataset es una agrupación de mensajes analizados dentro de un determinado proyecto. Estará compuesto por uno o más archivos. De forma similar a los proyectos, podremos pedir un listado paginado de datasets o solamente uno si conocemos su id.
All datasets
Retorna la lista de datasets asociados a un proyecto en específico. Opcionalmente, permite filtrar los resultados de acuerdo a los criterios descritos en el parámetro filters
{
project(id: "project_id") {
allDatasets(
first: 10,
after: 0,
filters: {
attributes: [{ key: "attribute_key_1", value: "attribute_value_1", type: DATASET_ATTRIBUTE }]
},
) {
id
name
createdAt
status
attributes {
key
value
type
}
}
}
}
Petición
Parámetro | Tipo | Obligatorio | Valor por defecto | Descripción |
---|---|---|---|---|
first | Int | no | 10 | Cantidad de elementos a devolver |
after | Int | no | 0 | Saltándose este número de elementos |
filters | DatasetFilters | no | - | Filtrados por estos criterios |
DatasetFilters
define determinados criterios para filtrar los resultados de la consulta dedatasets
. Por ejemplo, especificando el criterioattributes
nos devolverá una lista dedatasets
que cumplan con dichos valores.
Respuesta
{
"data": {
"project": {
"allDatasets": [
{
"id": "dataset_id",
"name": "Dataset Name",
"createdAt": "2020-01-11T11:00:00.000Z",
"status": "Completed",
"attributes": [
{
"key": "attribute_key_1",
"value": "attribute_value_1",
"type": DATASET_ATTRIBUTE,
}, …
],
}, …
]
}
}
}
Atributo | Tipo | Descripción |
---|---|---|
id | ID | ID del dataset |
name | String | Nombre del dataset |
createdAt | DateTime | Fecha de la primera subida |
status | DatasetStatus | Estado del dataset: Created , Processing , Completed , Error |
attributes | [DatasetAttribute] | Atributos basados en los tipos: DATASET_ATTRIBUTE , DOCUMENT_COLUMN , BENCHMARK_COLUMN |
Specific dataset
También podemos consultar un dataset determinado a través de su ID usando dataset
en lugar de allDatasets
{
project(id: "project_id") {
dataset(id: "dataset_id") {
id
name
createdAt
status
}
}
}
Petición
Parámetro | Tipo | Obligatorio | Valor por defecto | Descripción |
---|---|---|---|---|
id | ID | si | - | ID del dataset |
{
"data": {
"project": {
"dataset": {
"id": "dataset_id",
"name": "Dataset Name",
"createdAt": "2020-01-11T11:00:00.000Z",
"status": "Completed"
}
}
}
}
Respuesta
La respuesta será la misma pero acotada al dataset
con el ID especificado en la petición.
Dataset attributes
Es posible consultar todos los atributos de un dataset
, o bien filtrarlos a partir de un tipo específico
{
project(id: "project_id") {
allDatasets {
attributes {
key
value
type
}
}
dataset(id: "dataset_id") {
attributes(type: DOCUMENT_COLUMN) {
key
value
type
}
}
}
}
Petición
Parámetro | Tipo | Obligatorio | Valor por defecto | Descripción |
---|---|---|---|---|
type | DatasetAttributeType | no | - | Tipos de atributo: DATASET_ATTRIBUTE , DOCUMENT_COLUMN , BENCHMARK_COLUMN |
{
"data": {
"project": {
"allDatasets": [
{
"attributes": [
{
"key": "attribute_key_1",
"value": "attribute_value_1",
"type": DATASET_ATTRIBUTE,
}, {
"key": "attribute_key_2",
"value": "attribute_value_2",
"type": DOCUMENT_COLUMN,
}, …
],
}, …
],
"dataset": {
"attributes": [
{
"key": "attribute_key_2",
"value": "attribute_value_2",
"type": DOCUMENT_COLUMN,
}, …
]
}
}
}
}
Respuesta
Atributo | Tipo | Descripción |
---|---|---|
attributes | [DatasetAttribute] | Atributos basados en los tipos: DATASET_ATTRIBUTE , DOCUMENT_COLUMN , BENCHMARK_COLUMN |
DATASET_ATTRIBUTE
se refiere a pares clave-valor que describen propiedades deldataset
que lo enriquecen con información personalizada y a través de la cual es posible aplicar filtros de búsqueda (verallDatasets
endpoint)
DOCUMENT_COLUMN
se refiere a pares clave-valor que describen columnas de cada mensaje analizado deldataset
para la inserción de datos personalizados de acuerdo al modelo lingüístico. P. ej. NPS, Segmentación, etc.
BENCHMARK_COLUMN
Columna para analizar y comparar los distintos valores de la misma. Puedes comparar marcas, sucursales, localizaciones, etc. Filtros, gráficos y widgets comparativos del dashboard dependen directamente del contenido de esta columna.
Análisis
El uso principal de la API de CX es el análisis de documentos. Ofrecemos tres métodos para ello.
Single-document mode + save (recomendado)
{
project(id: "project_id") {
dataset(id: "dataset_id") {
analyze(
content: "Este texto se guardará y se podrá consultar en mi dashboard",
metadata: [{
columnKey: "dataset_custom_column",
value: "Este valor se guardará y se podrá consultar en mi dashboard"
}, … ]
) {
sentiment
categories
themes {
id
name
matchingCategories {
categories {
id
name
}
count
percent
}
}
}
}
}
}
Es una variante del método anterior. Podemos hacer la misma petición pero especificando también un dataset, en cuyo caso el documento se guardará en la base de datos. Si incluímos el parámetro metadata
, estos valores también se guardarán como datos de dicho documento, coincidiendo cada columnKey
con un atributo del dataset del tipo DOCUMENT_COLUMN
(es posible consultar los atributos de un dataset mediante su respectivo endpoint).
Petición
Parámetro | Tipo | Obligatorio | Descripción |
---|---|---|---|
content | String | sí | Texto a analizar |
metadata | MetadataInput | no | Datos personalizados a través de attributes del dataset |
Respuesta
{
"data": {
"project": {
"analyze": {
"sentiment": "POSITIVE",
"categories": [ "Página web", "Amigable", … ],
"themes": [
{
"id": "theme_id",
"name": "Theme name",
"matchingCategories": {
"categories": [
{
"id": "category_id",
"name": "Category name"
}
],
"count": 2,
"percent": 33.33,
}
}, …
],
}
}
}
}
Atributo | Tipo | Descripción |
---|---|---|
sentiment | Sentiment | Sentimiento del texto: POSITIVE , NEGATIVE , OBJECTIVE , UNKNOWN |
categories | [String] | Listado de categorías |
themes | [Theme] | Listado de temas configurados en el proyecto de los cuales han coincidido sus categorías |
Single-document mode
{
project(id: "project_id") {
analyze(content: "¡Me gusta mucho la web!") {
sentiment
categories
themes {
id
name
matchingCategories {
categories {
id
name
}
count
percent
}
}
}
}
}
Podemos analizar un único documento si especificamos el texto y un proyecto
Petición
Parámetro | Tipo | Obligatorio | Descripción |
---|---|---|---|
content | String | sí | Texto a analizar |
Respuesta
La respuesta será exactamente la misma a la anterior pero bajo el nivel del project
especificado en la petición.
Whole-file mode
La idea de este modo es subir un fichero (dataset) para analizar, esperar a que el análisis en bulk termine y descargar el fichero analizado. Los pasos serían:
- Crear un dataset (opcional, sólo si no está creado previamente)
{
createDataset(projectId: "project_id", name: "Project 2", contentColumnKey: "texto") {
id
}
}
Petición
Parámetro | Tipo | Obligatorio | Descripción |
---|---|---|---|
projectId | ID | sí | Id del proyecto al que añadir el dataset |
name | String | sí | Nombre del dataset |
contentColumnKey | String | sí | Nombre de la key o columna del .csv que contiene el texto a analizar |
{
createDataset {
id: "dataset_id"
}
}
Respuesta
Atributo | Tipo | Descripción |
---|---|---|
id | ID | Id del dataset creado |
{
datasetUploadUrl(id: "dataset_id")
}
- Obtener la url de subida del dataset
- Subir el fichero a la URL obtenida en el paso anterior. La URL está pre-firmada y tiene un tiempo de expiración de 1 hora. Para subir el fichero, sigue las indicaciones de la documentación de AWS. En particular no olvides especificar la cabeceras
Content-Type: text/csv
yContent-Length
- Esperar a que el análisis termine. Para ello el cliente hará una espera activa, es decir, hará peticiones al estado del dataset hasta que éste pase a
Completed
{
datasetDownloadUrl(id: "dataset_id")
}
- Descarga del archivo analizado. Una vez el análisis se haya completado, el fichero estará disponible para la descarga. La URL de descarga contendrá el archivo JSON con todos los documentos analizados. Por seguridad, la URL caducará después de una hora de su generación.
Single-document mode + Filter Routing & Save
# request
{
client {
analyze(
content: "¡Me gusta la web!",
filters: {
attributes: [
{ key: "Fuente", value: "Benchmark", type: DATASET_ATTRIBUTE },
{ key: "Estudio", value: "Clientes", type: DATASET_ATTRIBUTE },
{ key: "Subestudio", value: "Website", type: DATASET_ATTRIBUTE },
]
},
metadata: [{
columnKey: "dataset_custom_column",
value: "Este valor se guardará y se podrá consultar en mi dashboard"
}, … ]
) {
projectId
datasetAnalysisResults {
datasetId
analysisResult {
sentiment
categories
tags
themes {
name
matchingCategories {
categories {
id
name
}
count
percent
}
groupingTags {
id
name
orderLevel
}
}
}
}
}
}
}
Permite analizar y guardar en múltiples datasets en una única operación.
Podemos solicitar un análisis especificando además del texto, un filtro de datasets para que tras el análisis se almacene el resultado en todos los datasets que contengan los DATASET_ATTRIBUTE especificados.
El texto se analizará múltiples veces, una para cada dataset, usando las reglas correspondientes al proyecto al que pertenezca el dataset.
Petición
Parámetro | Tipo | Obligatorio | Descripción |
---|---|---|---|
content | String | sí | Texto a analizar |
filters | AtributesInput | no | Filtro de datasets que tengan todos los DATASET_ATTRIBUTE especificados |
metadata | MetadataInput | no | Datos personalizados a través de attributes del dataset |
Respuesta
# response
{
"data": {
"client": {
"analyze": [
{
"projectId": "project_id",
"datasetAnalysisResults": [
{
"datasetId": "datset_id",
"analysisResult": {
"sentiment": "POSITIVE",
"categories": [],
"tags": [
"web"
],
"themes": [
{
"name": "theme_name",
"matchingCategories": {
"categories": [
{
"id": "category_id",
"name": "Category name"
}
],
"count": 1,
"percent": 25,
},
"groupingTags": [
{
"id": "theme_tag_id",
"name": "theme_tag_name",
"orderLevel": 1,
}
]
}
]
}
}
]
}
]
}
}
}
La respuesta incluirá solo los resultados correspondientes a los datasets para los que fue procesado.
Atributo | Tipo | Descripción |
---|---|---|
sentiment | Sentiment | Sentimiento del texto: POSITIVE , NEGATIVE , OBJECTIVE , UNKNOWN |
categories | [String] | Listado de categorías |
themes | [Theme] | Listado de temas configurados en el proyecto de los cuales han coincidido sus categorías durante el análisis |
Referencia
HTTP HEADERS
{
"Authorization": "Bearer YOUR_TOKEN"
}
La especificación completa del esquema GraphQL puede consultarse en este playground: https://api.cx.sentisis.io/graphql