Reten Docs
Operadores

Upsert por Lote

Crea o actualiza hasta 500 operadores en una sola operación, con reporte de resultado por item.

POST /api/integration/operators/batch-upsert

Crea o actualiza (upsert) hasta 500 operadores en una sola operación. Es el punto de entrada de integración para cargar la dotación de operadores (vendedores, activadores, agentes de call center) desde un sistema externo.

Auth: Requerida — permiso operator:batch-upsert

Éxito parcial. Una vez que el cuerpo pasa la validación de formato, cada item se procesa de forma independiente: los que se pueden persistir se persisten, y los que fallan se reportan en errors[] con un código estable — un item que falla no deshace a los demás. Revisa siempre summary.failed y errors en la respuesta.

Clave del upsert

Cada item se busca entre los operadores existentes por el par type + external_id. Si existe un operador con ese par se actualiza (cuenta en summary.updated); si no, se crea (summary.created). El mismo external_id bajo otro type es otro operador. En la actualización, metadata se fusiona con la existente: las claves enviadas sobrescriben las del mismo nombre, las no enviadas se conservan, y enviar una clave con null la elimina. La fusión es recursiva (RFC 7386 JSON Merge Patch): los objetos anidados se fusionan nivel por nivel, así podés actualizar una clave dentro de un objeto anidado sin reenviar sus hermanas. Cualquier otro valor reemplaza al anterior por completo, incluidos los arrays, que nunca se fusionan elemento a elemento. Enviar metadata: null vacía la bolsa entera.

Cuerpo de la Petición

CampoTipoRequeridoDescripción
itemsobject[]SíLista de operadores a escribir (mínimo 1, máximo 500).

Cada elemento de items:

CampoTipoRequeridoDescripción
typeenumSíTipo de operador: SALESMAN, ACTIVATOR o CALLCENTER. Parte de la clave del upsert.
external_idstringSíIdentificador del operador en el sistema externo. Parte de la clave del upsert.
namestringSíNombre del operador.
emailstringNoCorreo electrónico. Debe ser un correo válido y no puede estar en uso por otro operador.
phonestringNoNúmero de teléfono.
metadataobjectNoJSON arbitrario. Se fusiona recursivamente con la metadata existente.

Ejemplo

curl -X POST "https://api.reten.ai/api/integration/operators/batch-upsert" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "type": "SALESMAN",
        "external_id": "OP-001",
        "name": "Juan Pérez",
        "email": "juan.perez@example.com",
        "phone": "+56912345678",
        "metadata": { "zone": "north" }
      },
      {
        "type": "ACTIVATOR",
        "external_id": "OP-002",
        "name": "Ana López"
      },
      {
        "type": "CALLCENTER",
        "external_id": "OP-003",
        "name": "Carla Soto",
        "email": "juan.perez@example.com"
      }
    ]
  }'
const response = await fetch(
  "https://api.reten.ai/api/integration/operators/batch-upsert",
  {
    method: "POST",
    headers: {
      "x-api-key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      items: [
        { type: "SALESMAN", external_id: "OP-001", name: "Juan Pérez", email: "juan.perez@example.com", phone: "+56912345678", metadata: { zone: "north" } },
        { type: "ACTIVATOR", external_id: "OP-002", name: "Ana López" },
        { type: "CALLCENTER", external_id: "OP-003", name: "Carla Soto", email: "juan.perez@example.com" },
      ],
    }),
  },
);
const result = await response.json();
import requests

response = requests.post(
    "https://api.reten.ai/api/integration/operators/batch-upsert",
    headers={"x-api-key": "YOUR_API_KEY"},
    json={
        "items": [
            {"type": "SALESMAN", "external_id": "OP-001", "name": "Juan Pérez", "email": "juan.perez@example.com", "phone": "+56912345678", "metadata": {"zone": "north"}},
            {"type": "ACTIVATOR", "external_id": "OP-002", "name": "Ana López"},
            {"type": "CALLCENTER", "external_id": "OP-003", "name": "Carla Soto", "email": "juan.perez@example.com"},
        ],
    },
)
result = response.json()

Respuesta 200 OK

En este ejemplo, OP-001 ya existía y se actualizó, OP-002 se creó, y OP-003 falló porque su email ya está en uso por otro operador:

{
  "summary": {
    "total": 3,
    "created": 1,
    "updated": 1,
    "failed": 1
  },
  "errors": [
    {
      "index": 2,
      "identifier": "OP-003",
      "error": "upsert_error",
      "message": "Email juan.perez@example.com already exists in operator 9c4b2e7a-1d5f-4a8b-b3c6-7e8f9a0b1c2d"
    }
  ]
}

Campos de la respuesta

CampoTipoDescripción
summary.totalnumberTotal de items enviados.
summary.creatednumberItems insertados (operadores nuevos).
summary.updatednumberItems actualizados (operadores existentes modificados).
summary.failednumberItems que no se pudieron persistir.
errorsobject[]Detalle por item fallido (vacío si todos tuvieron éxito).
errors[].indexnumberPosición (base 0) del item dentro del array items enviado.
errors[].identifierstringEl external_id del item fallido.
errors[].errorstringCódigo de error estable (ver tabla siguiente).
errors[].messagestringMensaje legible para humanos/logs con la causa.

Códigos de error por item

errorSignificado
upsert_errorEl item no se pudo persistir; message trae la causa (p. ej. un email ya en uso por otro operador).

Errores (de la request completa)

StatusDescripción
400items vacío o con más de 500 elementos; un item que no cumple el formato (campo requerido ausente, tipo o valor de enum inválido); o una propiedad no declarada aquí.
401Clave de API faltante o inválida.
403La clave de API no tiene el permiso operator:batch-upsert.