Tasks

Namespace tasks plus the tasks blueprint (actionstools_app/tasks/), table em_actions_tasks (_staging outside production), key (instance_id, uuid). All routes require a JWT.

Routes

Method Path Behaviour

GET

/tasks/

With X-Actions-TargetUUID: tasks of that target, newest update first, each with latest_comments and comments_count. Without it: the task grid, filtered by ?q= / ?order_by= (api/index.adoc#pagination), defaulting to all tasks of instance 8001. meta.count comes from the counters (Task counters).

POST

/tasks/

Create. Required body fields: created_by_name, title, assignee_name, assignee_uuid, assignee_type (user/group), priority, status, plus instance_id (header). See Creating a task.

PATCH

/tasks/

Bulk update: {"<uuid>": {fields…​}, …​}. Each update goes through perform_update.

DELETE

/tasks/

Bulk soft delete: {"uuids": […​]}. Returns 404 listing the uuids that do not exist, without deleting anything.

GET

/tasks/<uuid>/

Single task (X-InstanceId required).

PUT

/tasks/<uuid>/

Update (same required fields as create).

DELETE

/tasks/<uuid>/

Soft delete.

POST

/tasks/filters/counts/

{"queries": [{"filters_query": […​filters]}, …​]} → the same list with count set on each.

POST

/tasks/filters/counts/assignees/

{"assignees": […​], "filters_query": […​]} → {"<assignee_uuid>": count}.

GET

/tasks/<uuid>/events/

Change history of a task, proxied from the event logger’s /es-changelog.

Creating a task

{
  "title": "Collect missing bank statement",
  "body": "<p>Statement for March is missing</p>",
  "status": "new",
  "priority": "high",
  "assignee_type": "group",
  "assignee_uuid": "CUST_SERVICE",
  "assignee_name": "Customer Service",
  "created_by_name": "agent@payability.com",
  "dueDate": "2026-11-02T17:00:00Z",
  "supplier_name": "ACME Supplies LLC",
  "task_url_template": "https%3A%2F%2Finternal.payability.com%2F%23%2Ftasks%3FtaskUuid%3D%7B%7D",
  "_mode": "sync"
}
  • item_number is assigned from the global counter em_actions_counters (atomic increment).

  • dueDate may be a timestamp or any date string dateutil parses; it is stored as a Unix timestamp. due_date and dueDate are kept in sync (sanitize_task_for_iav2).

  • priority_int is derived from priority (low 10 … critical 40) for sorting.

  • task_url_template (URL-encoded, {} replaced by the task uuid) is used in notifications. Production defaults to https://internal.payability.com/#/tasks?taskUuid={}.

  • Duplicate guard: if one of the last three tasks created on the instance has the same body, title, target, assignee, owner, status, type and priority, that task is returned instead of a new one. This absorbs double clicks and client retries.

"_mode": "async" in the body queues the whole creation on the Worker and returns immediately with meta.message = "object has been successfully queued to create" and no data.

Async mode calls get_auth_data() on the Worker, where there is no Flask request, so the assignee notification step fails there (logged to Sentry). The task itself is created first. This predates the SAM migration; prefer the default sync mode until it is fixed.

Updates and notifications

perform_update diffs the new fields against the stored task:

  • Changing item_number, instance_id, uuid, created_at, created_by_name, owner_uuid, type, target_type, body or title requires the caller to be the owner or a CRM_ADMIN (checked against the Payability internal API).

  • Setting status to done reassigns the task to its creator.

  • If the caller is logged in, send_events_and_notifications is queued: two event-logger entries (task and supplier) and, when task_url_template was sent, notifications to the assignee, the owner and followers. See Slack and Messaging (email).