Bulk Create Activities
Create multiple activities in a single request.
POST /api/activities/bulk
Create up to 200 activities in a single request. Each item follows the same schema as the single Create Activity endpoint.
Auth: Required — activity:create permission
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
items | array | Yes | Array of activity objects (min 1, max 200) |
Each item in items has the same fields as the Create Activity request body:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | TASK |
reason | string | Yes | Reason code: lowercase letters, digits and _ only (^[a-z0-9_]+$), max 100 characters. A code that is not in the tenant's reason catalog is added to it on the fly |
channel | string | Yes | SALESMAN, CALLCENTER |
commerce_id | UUID | Conditional | Target commerce ID. Send it or commerce_external_id: at least one is required. If you send both, commerce_id wins |
commerce_external_id | string | Conditional | Target commerce by external ID (max 255 characters). If no commerce has it, the API creates a placeholder commerce with that external ID |
idempotency_key | string | Yes | Unique key per tenant (prevents duplicates) |
scheduled_at | ISO 8601 | Yes | When the activity should be executed |
intent_category | string | No | ONBOARDING, MARKETING, COLLECTION, RETENTION, NOTIFICATION, SURVEY or VALIDATION. Default NOTIFICATION |
suggested_execution_time | ISO 8601 | No | Suggested time for execution |
user_status | string | No | User status context |
score | integer | No | Priority score |
assignment_reason | string | No | Reason for the assignment |
activity_details | object | Yes, for TASK | Route and operator assignment (see Create Activity). A TASK item without it fails with validation_error |
Example
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()Response 201 Created
Once the body passes validation, the API always responds 201 Created, even if every item fails. Items are processed one at a time, in order, and a failed item does not affect the others. The body reports the outcome of each item; index is the item's 0-based position in items.
| Field | Type | Description |
|---|---|---|
summary | object | Counters: total (items received), created, skipped and failed |
items | array | Created activities: index, idempotencyKey, activityId |
skipped | array | Items whose idempotency_key already belongs to an activity of the active project: index, idempotencyKey, existingActivityId. Nothing is created or changed, whatever the status of that activity |
errors | array | Failed items: index, idempotencyKey, error, message |
error is one of these codes, and message is the API's message for that item:
error | When | Example message |
|---|---|---|
validation_error | The item passes body validation but breaks a creation rule | Se 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_found | commerce_id, assigned_route_id or assigned_operator_id does not exist | Commerce not found, Route not found, Operator not found |
creation_error | Any other failure — for example, the idempotency_key is already used by an activity of another project of the tenant | La clave de idempotencia ya está en uso. |
An idempotency_key already used in another project is reported as a creation_error item, never as an HTTP 409.
Example for a batch of three items: the first is created, the second has an idempotency_key that already exists in the project, and the third references a commerce that does not exist.
{
"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"
}
]
}Error Responses
| Status | Description |
|---|---|
400 | Invalid body: items is empty or has more than 200 elements, or an item has an invalid or missing field, or a field the endpoint does not accept. Nothing is created |
401 | Missing or invalid authentication token |
403 | Missing activity:create permission |