Reten Docs
Actividades

Crear Actividades en Lote

Crear múltiples actividades en una sola solicitud.

POST /api/activities/bulk

Crea hasta 200 actividades en una sola solicitud. Cada elemento sigue el mismo esquema que el endpoint individual Crear Actividad.

Auth: Requerida — permiso activity:create

Cuerpo de la Solicitud

CampoTipoRequeridoDescripción
itemsarraySíArray de objetos de actividad (min 1, max 200)

Cada elemento en items tiene los mismos campos que el cuerpo de la solicitud de Crear Actividad:

CampoTipoRequeridoDescripción
typestringSíTASK
reasonstringSíCódigo de razón: solo minúsculas, dígitos y _ (^[a-z0-9_]+$), máximo 100 caracteres. Un código que no está en el catálogo de razones del tenant se agrega al vuelo
channelstringSíSALESMAN, CALLCENTER
commerce_idUUIDCondicionalID del comercio destino. Envía este campo o commerce_external_id: al menos uno es obligatorio. Si envías los dos, gana commerce_id
commerce_external_idstringCondicionalComercio destino por ID externo (máximo 255 caracteres). Si ningún comercio lo tiene, la API crea un comercio placeholder con ese ID externo
idempotency_keystringSíClave única por tenant (previene duplicados)
scheduled_atISO 8601SíCuándo se debe ejecutar la actividad
intent_categorystringNoONBOARDING, MARKETING, COLLECTION, RETENTION, NOTIFICATION, SURVEY o VALIDATION. Por defecto NOTIFICATION
suggested_execution_timeISO 8601NoHora sugerida de ejecución
user_statusstringNoContexto de estado del usuario
scoreintegerNoPuntaje de prioridad
assignment_reasonstringNoRazón de la asignación
activity_detailsobjectSí, para TASKAsignación de ruta y operador (ver Crear Actividad). Un elemento TASK sin este campo falla con validation_error

Ejemplo

curl -X POST https://api.reten.ai/api/activities/bulk \
  -H "Authorization: Bearer <token>" \
  -H "x-tenant-id: <tenant-id>" \
  -H "x-project-id: <project-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {
        "type": "TASK",
        "reason": "monthly_visit",
        "channel": "SALESMAN",
        "commerce_id": "880e8400-e29b-41d4-a716-446655440000",
        "idempotency_key": "task-2025-01-15-001",
        "scheduled_at": "2025-01-16T09:00:00.000Z",
        "activity_details": {
          "assigned_route_id": "dd0e8400-e29b-41d4-a716-446655440000",
          "assigned_operator_id": "cc0e8400-e29b-41d4-a716-446655440000"
        }
      },
      {
        "type": "TASK",
        "reason": "monthly_visit",
        "channel": "CALLCENTER",
        "commerce_external_id": "SHOP-0042",
        "idempotency_key": "task-2025-01-15-002",
        "scheduled_at": "2025-01-16T09:00:00.000Z",
        "activity_details": {
          "assigned_route_external_id": "ROUTE-07",
          "assigned_operator_external_id": "AGENT-15"
        }
      }
    ]
  }'
const response = await fetch("https://api.reten.ai/api/activities/bulk", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <token>",
    "x-tenant-id": "<tenant-id>",
    "x-project-id": "<project-id>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    items: [
      {
        type: "TASK",
        reason: "monthly_visit",
        channel: "SALESMAN",
        commerce_id: "880e8400-e29b-41d4-a716-446655440000",
        idempotency_key: "task-2025-01-15-001",
        scheduled_at: "2025-01-16T09:00:00.000Z",
        activity_details: {
          assigned_route_id: "dd0e8400-e29b-41d4-a716-446655440000",
          assigned_operator_id: "cc0e8400-e29b-41d4-a716-446655440000",
        },
      },
      {
        type: "TASK",
        reason: "monthly_visit",
        channel: "CALLCENTER",
        commerce_external_id: "SHOP-0042",
        idempotency_key: "task-2025-01-15-002",
        scheduled_at: "2025-01-16T09:00:00.000Z",
        activity_details: {
          assigned_route_external_id: "ROUTE-07",
          assigned_operator_external_id: "AGENT-15",
        },
      },
    ],
  }),
});
const result = await response.json();
import requests

response = requests.post(
    "https://api.reten.ai/api/activities/bulk",
    headers={
        "Authorization": "Bearer <token>",
        "x-tenant-id": "<tenant-id>",
        "x-project-id": "<project-id>",
    },
    json={
        "items": [
            {
                "type": "TASK",
                "reason": "monthly_visit",
                "channel": "SALESMAN",
                "commerce_id": "880e8400-e29b-41d4-a716-446655440000",
                "idempotency_key": "task-2025-01-15-001",
                "scheduled_at": "2025-01-16T09:00:00.000Z",
                "activity_details": {
                    "assigned_route_id": "dd0e8400-e29b-41d4-a716-446655440000",
                    "assigned_operator_id": "cc0e8400-e29b-41d4-a716-446655440000",
                },
            },
            {
                "type": "TASK",
                "reason": "monthly_visit",
                "channel": "CALLCENTER",
                "commerce_external_id": "SHOP-0042",
                "idempotency_key": "task-2025-01-15-002",
                "scheduled_at": "2025-01-16T09:00:00.000Z",
                "activity_details": {
                    "assigned_route_external_id": "ROUTE-07",
                    "assigned_operator_external_id": "AGENT-15",
                },
            },
        ],
    },
)
result = response.json()

Respuesta 201 Created

Una vez que el cuerpo pasa la validación, la API responde siempre 201 Created, aunque fallen todos los elementos. Los elementos se procesan de a uno, en orden, y un elemento que falla no afecta a los demás. El cuerpo informa el resultado de cada elemento; index es la posición del elemento en items, contando desde 0.

CampoTipoDescripción
summaryobjectContadores: total (elementos recibidos), created, skipped y failed
itemsarrayActividades creadas: index, idempotencyKey, activityId
skippedarrayElementos cuya idempotency_key ya pertenece a una actividad del proyecto activo: index, idempotencyKey, existingActivityId. No se crea ni se modifica nada, sea cual sea el estado de esa actividad
errorsarrayElementos que fallaron: index, idempotencyKey, error, message

error es uno de estos códigos, y message es el mensaje de la API para ese elemento:

errorCuándoEjemplo de message
validation_errorEl elemento pasa la validación del cuerpo pero incumple una regla de creaciónSe requiere al menos uno de los campos commerce_id o commerce_external_id., TASK activities require activityDetails, assigned_route_id is required for TASK activities
not_foundcommerce_id, assigned_route_id o assigned_operator_id no existeCommerce not found, Route not found, Operator not found
creation_errorCualquier otra falla — por ejemplo, la idempotency_key ya la usa una actividad de otro proyecto del tenantLa clave de idempotencia ya está en uso.

Una idempotency_key ya usada en otro proyecto se informa como un elemento creation_error, nunca como un HTTP 409.

Ejemplo para un lote de tres elementos: el primero se crea, el segundo tiene una idempotency_key que ya existe en el proyecto y el tercero referencia un comercio que no existe.

{
  "summary": {
    "total": 3,
    "created": 1,
    "skipped": 1,
    "failed": 1
  },
  "items": [
    {
      "index": 0,
      "idempotencyKey": "task-2025-01-15-001",
      "activityId": "ee0e8400-e29b-41d4-a716-446655440000"
    }
  ],
  "skipped": [
    {
      "index": 1,
      "idempotencyKey": "task-2025-01-14-007",
      "existingActivityId": "ff0e8400-e29b-41d4-a716-446655440000"
    }
  ],
  "errors": [
    {
      "index": 2,
      "idempotencyKey": "task-2025-01-15-003",
      "error": "not_found",
      "message": "Commerce not found"
    }
  ]
}

Respuestas de Error

EstadoDescripción
400Cuerpo inválido: items está vacío o tiene más de 200 elementos, o un elemento tiene un campo inválido o faltante, o un campo que el endpoint no acepta. No se crea nada
401Token de autenticación faltante o inválido
403Falta el permiso activity:create