> For the complete documentation index, see [llms.txt](https://boundaryai.gitbook.io/boundaryai-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://boundaryai.gitbook.io/boundaryai-docs/boundaryai-docs-es/api-y-webhooks/pushing-feedback.md).

# Envío de feedback

Envíos individuales, cargas masivas de datos, ingesta de archivos, desduplicación y fechas retroactivas.

Todo sobre cómo ingresar datos, más allá de la versión de cinco minutos en [Primeros pasos](/boundaryai-docs/boundaryai-docs-es/api-y-webhooks/getting-started.md): qué ruta de ingesta usar, cómo se comportan la desduplicación y la asignación retroactiva de fechas, y qué sucede después del envío.

***

### Tres formas de entrada

| Ruta                       | Úselo cuando                                                                                                     | Comportamiento                                                                                                                                                                |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /feedback/push`      | Un goteo constante en un solo campo: cada ticket, reseña o llamada al cerrarse.                                  | Sincrónico; la respuesta informa `insertados` y `skipped_duplicates`. Cada elemento se convierte en su propio encuestado.                                                     |
| `POST /feedback/push/bulk` | Varios campos en una sola llamada: un registro completo por encuestado, cargas retroactivas, trabajos por lotes. | Asíncrono **202** con una `task_id` y `status_url` por defecto; consulte hasta `completado`. Los lotes pequeños pueden ejecutarse sincrónicamente con `"sync": true` (abajo). |
| `POST /feedback/upload`    | Ya tiene una exportación CSV o XLSX.                                                                             | Carga multipartita; el mismo flujo de tarea asíncrona que en el modo masivo.                                                                                                  |

Las tres llegan al mismo lugar y activan el mismo análisis. Prefiera el envío masivo frente a repetir envíos individuales: una sola llamada para miles de elementos es más amable con sus límites de tasa (60/minuto por clave por defecto) y con la plataforma.

#### Envío masivo: un encuestado, varios campos

`/feedback/push/bulk` toma una lista de `campos`, cada una con su propio `content` matriz, y registra todo el lote como las respuestas de un solo encuestado. Así es como envía un registro completo (el comentario abierto *y* su puntuación NPS) como una fila y no como líneas desconectadas; en cambio, el endpoint de envío único trata cada elemento como su propio encuestado. Cuando cada registro corresponde a una persona distinta, envíe un registro por llamada masiva (el modo sincrónico de abajo está diseñado exactamente para eso). Un `source_reference` (hasta 255 caracteres) se almacena en cada fila del lote, para que pueda etiquetar qué sistema o trabajo lo produjo.

#### Envío masivo sincrónico

Las plataformas de integración y los receptores de webhook suelen entregar un registro por llamada y no pueden consultar un task id. Añada `"sync": true` y el lote se ejecuta en línea, devolviendo **200** con el resultado completo por campo en lugar de un task id:

```bash
curl -X POST https://boundaryai-ingest-279197672085.europe-west9.run.app/api/input/feedback/push/bulk \
  -H "Authorization: Bearer $BAI_API_KEY" -H "Content-Type: application/json" \\
  -H "Idempotency-Key: ticket-58121-v1" \
  -d '{
    "feedback_group_id": "1842",
    "source_id": "9021",
    "sync": true,
    "source_reference": "crm-webhook",
    "fields": [
      {"field_id": "31245", "content": [{"text": "El soporte resolvió mi problema en una sola llamada.", "external_id": "ticket-58121"}]},
      {"field_id": "31246", "content": [{"text": "9", "external_id": "ticket-58121"}]}
    ]
  }'
```

Las reglas que mantienen esto seguro:

* **Cada fila debe llevar un `external_id`**; de lo contrario, la llamada se rechaza con `SYNC_REQUIRES_EXTERNAL_ID` (400). Un envío en línea puede verse interrumpido por su tiempo de espera, y el id es lo que permite que un reintento converja en las mismas filas en lugar de duplicarlas.
* **Solo lotes pequeños**: hasta 50 elementos en hasta 20 campos. Los lotes más grandes y las solicitudes que llegan mientras la capacidad en línea está ocupada vuelven silenciosamente a la ruta asíncrona y devuelven **202** con un task id, así que un cliente que siempre envía `sincronización` sigue funcionando.
* **Códigos de estado honestos**: si ningún campo aceptó nada, obtiene **400** `PUSH_FAILED` con el detalle por campo, en lugar de un 200 que oculta las filas rechazadas. Un envío que supera su límite de 60 segundos devuelve **504** `SYNC_PUSH_TIMEOUT`; las filas ya escritas permanecen escritas, así que reintente con los mismos `external_id` valores.

***

### Elementos estructurados: los campos que valen la pena

Un elemento en `content` puede ser una cadena simple, pero las integraciones de producción deben enviar objetos:

```json
{"text": "Soporte resolvió mi problema en una sola llamada.",
 "external_id": "ticket-58121",
 "customer_id": "cus_310",
 "channel": "support",
 "language": "en",
 "rating": 5,
 "occurred_at": "2026-07-12T09:30:00Z"}
```

* **`external_id`** es su identificador estable, y es lo que hace seguros los reintentos: dentro de un campo, un segundo envío con un `external_id` que ya existe se **omitido**, y la respuesta lo cuenta bajo `skipped_duplicates` con los IDs en `duplicate_external_ids`. Tenga en cuenta que esto es una omisión, no una actualización: enviar un texto corregido bajo el mismo `external_id` deja el original en su lugar. Sin un `external_id`, un lote reproducido significa comentarios duplicados que contaminan su análisis.
* **`occurred_at`** asigna la fecha retroactiva del elemento al momento en que realmente ocurrió el comentario. Esto importa sobre todo para [grupos con seguimiento temporal](/boundaryai-docs/boundaryai-docs-es/analizando-sus-comentarios/tracking-feedback-over-time-evolution.md): un ticket de junio enviado en julio cae en el periodo de junio, no en el de julio. Si se omite, los elementos se fechan según la hora de llegada.
* **`customer_id`** vincula los elementos con una persona a través de las fuentes, impulsa [borrado masivo](#erasing-and-listing-what-you-pushed), y se empareja con la `external_id` en [invitaciones](/boundaryai-docs/boundaryai-docs-es/api-y-webhooks/sending-invites.md) para que pueda unir el ciclo completo en su almacén de datos.
* **`channel`**, **`language`**, **`rating`** se convierten en metadatos de segmentación, exactamente igual que las columnas de metadatos en una [carga](/boundaryai-docs/boundaryai-docs-es/incorporacion-de-sus-comentarios/uploading-an-existing-dataset.md).

Los campos numéricos (NPS, valoraciones) toman su valor como el `texto` (`"9"`); los valores no finitos se rechazan en la ingesta.

Dos capas de seguridad para reintentos se combinan: `external_id` desduplica a nivel de elemento para siempre, y el **`Idempotency-Key`** encabezado hace que toda la solicitud pueda reproducirse durante 24 horas (misma clave = misma respuesta, sin reprocesamiento). Use ambos; las redes fallan a mitad de solicitud más a menudo de lo que a nadie le gusta.

***

### Qué sucede después de un envío

1. Los elementos se limpian, se detecta el idioma y se ponen automáticamente en cola para su análisis; no hay ninguna llamada de "run analysis" que hacer.
2. Los monitores de grupo con **Cubrir automáticamente nuevas fuentes** on ya están vigilando fuentes creadas por API ([Supervisión personalizada](/boundaryai-docs/boundaryai-docs-es/analizando-sus-comentarios/custom-monitoring.md)), así que las coincidencias del monitor y las alertas se activan desde la primera pasada.
3. Cuando termina la pasada, el **`analysis.completed`** webhook se dispara, y `GET /sources/{id}/analysis` devuelve la distribución de sentimiento, los temas y las coincidencias del monitor. El análisis tarda de minutos a decenas de minutos dependiendo del volumen; hasta que una ejecución se completa, la lectura devuelve `analysis_status: "none"` con cifras en cero que no son resultados.
4. Para una fuente en un grupo que sigue el feedback a lo largo del tiempo, la lectura agrega cada periodo analizado (el mismo corpus que la vista General del panel) y devuelve `analysis_status: "available"` con `analysis_id: null`; clave en `analysis_status`, nunca en `analysis_id`.
5. Los datos participan en todo lo demás: Temas agrupados, periodos de evolución, informes y paneles, indistinguibles de los datos de encuestas o de carga.

Enviar consume el **Cupo de uso** cuando se analiza, la misma medición que cualquier otro análisis en la plataforma; consulte [Configuración](/boundaryai-docs/boundaryai-docs-es/cuenta-y-administracion/updating-settings.md#usage). La respuesta del envío `rows_accepted` es el número de filas admitidas (`aps_deducted` es un alias heredado de ello); no se cobra nada en el momento del envío, y las claves de prueba nunca se cobran.

***

### Borrar y listar lo que envió

Usted sigue teniendo el control de los datos que creó su integración:

* `GET /feedback` lista elementos enviados por API (filtre por `source_id` / `field_id`, paginado por cursor), útil para trabajos de conciliación. Nunca incluye respuestas nativas de encuestas.
* `POST /feedback/erase` elimina masivamente por `external_ids` o por `customer_id`, que es la parte mecánica de cumplir una solicitud de supresión del RGPD: una llamada elimina el feedback enviado por API de una persona en todas las fuentes. Solo afecta a las filas enviadas por API; las respuestas de encuestas y las cargas tienen sus propios flujos de eliminación del producto.

***

### Lista de verificación de integración

* Una clave por integración, `push` alcance, `Idempotency-Key` en cada escritura.
* Envíe siempre `external_id` y `occurred_at`; su yo futuro haciendo una resincronización se lo agradecerá.
* Use el modo masivo para cualquier cosa de más de unos pocos elementos; consulte el `status_url` o suscríbase a `content.pushed`. Use `sync: true` cuando quien llama necesita el resultado en la misma respuesta.
* Suscríbase a `analysis.completed` en lugar de consultar el endpoint de análisis.
* Asocie `customer_id` al mismo identificador que usa su CRM, para que el borrado y las uniones de invitaciones sigan siendo operaciones de una sola llamada.
