Create Activity
Create a new activity assigned to a commerce.
POST /api/activities
Create a new TASK activity with commerce assignment.
Auth: Required — activity:create permission
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, with a label derived from the code (monthly_visit → Monthly visit) |
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 | Type-specific details — shape depends on type (see below) |
activity_details
For type: "TASK", the activity_details object carries route/operator assignment and is required. Both a route and an operator are required, each by ID or by external ID; if you send both forms, the ID wins. An external ID that no route or operator has makes the API create a placeholder with it.
| Field | Type | Required | Description |
|---|---|---|---|
assigned_route_id | UUID | Conditional | Assigned route. Send it or assigned_route_external_id |
assigned_route_external_id | string | Conditional | Assigned route by external ID (max 255 characters) |
assigned_operator_id | UUID | Conditional | Assigned operator. Send it or assigned_operator_external_id |
assigned_operator_external_id | string | Conditional | Assigned operator by external ID (max 255 characters) |
Example
curl -X POST https://api.reten.ai/api/activities \
-H "Authorization: Bearer <token>" \
-H "x-tenant-id: <tenant-id>" \
-H "x-project-id: <project-id>" \
-H "Content-Type: application/json" \
-d '{
"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"
}
}'const response = await fetch("https://api.reten.ai/api/activities", {
method: "POST",
headers: {
"Authorization": "Bearer <token>",
"x-tenant-id": "<tenant-id>",
"x-project-id": "<project-id>",
"Content-Type": "application/json",
},
body: JSON.stringify({
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",
},
}),
});
const activity = await response.json();import requests
response = requests.post(
"https://api.reten.ai/api/activities",
headers={
"Authorization": "Bearer <token>",
"x-tenant-id": "<tenant-id>",
"x-project-id": "<project-id>",
},
json={
"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",
},
},
)
activity = response.json()Response 201 Created
If an activity of the active project already has that idempotency_key, the API creates nothing and responds 200 OK with the existing activity.
{
"id": "ee0e8400-e29b-41d4-a716-446655440000",
"projectId": "3f6c2b1a-8d4e-4f7a-9b2c-5e1d0a7f6c34",
"type": "TASK",
"reason": {
"code": "monthly_visit",
"label": "Visita Mensual"
},
"channel": "SALESMAN",
"status": "READY",
"userStatus": null,
"idempotencyKey": "task-2025-01-15-001",
"scheduledAt": "2025-01-16T09:00:00.000Z",
"commerceAssignment": {
"commerceId": "880e8400-e29b-41d4-a716-446655440000"
}
}Error Responses
| Status | Description |
|---|---|
400 | Validation error: a missing or invalid field, or a field the endpoint does not accept |
400 | Neither commerce_id nor commerce_external_id — Se requiere al menos uno de los campos commerce_id o commerce_external_id. |
400 | TASK without activity_details — TASK activities require activityDetails |
400 | activity_details without a route or without an operator — assigned_route_id is required for TASK activities / assigned_operator_id is required for TASK activities |
400 | The assigned route or operator is inactive — Assigned route is not active / Assigned operator is not active |
400 | user_status is not an active code of the tenant's retention status catalog — User retention status "<code>" is not configured for this tenant |
404 | commerce_id, assigned_route_id or assigned_operator_id does not exist — Commerce not found / Route not found / Operator not found |
409 | The idempotency_key is already used by an activity of another project of the tenant — La clave de idempotencia ya está en uso. |