Skip to main content

Tasks & schedules

A task is Loca.fi's assignable unit of work: "audit the promo shelf in Store 1 by Friday", broken into checklist steps (task actions) and assigned to a user. Actions can be free-form manual steps or links to other domain records — complete the linked order or stocktake and the task ticks itself off. Task schedules group related tasks into a named run.

Read Tasks & schedules first for the state machines — this guide is the doing, that page is the understanding.

Not background tasks

Tasks here are human work assignments. They are unrelated to background tasks — the server-side async job records returned by long-running order and stocktake operations. For those, see Async operations.

Overview

  1. Set up action types (once) — five system types are pre-seeded per organisation; add custom ones at POST api/taskactiontypes/createtaskactiontype.
  2. Create the task with its actions and assignee — POST api/tasks/createtask.
  3. Work the queue — the assignee polls getmytasks and progresses actions with updatetaskactionstatus; the task auto-starts and auto-completes as its actions move.
  4. Optionally group tasks under a schedule — POST api/taskschedules/createtaskschedule, then pass TaskScheduleId when creating each child task.

All endpoints are gated on the Task module permission, with two deliberate exceptions open to any authenticated user: the "my work queue" reads (getmytasks, getmyopentaskcount, and their schedule equivalents) and updatetaskstatus, which lets an assignee progress their own task without Task update permission.

Prerequisites

  • A token with Task module permission — see Getting started.
  • The tasks concepts page — the ToDo → InProgress → Completed/Cancelled state machine and the AutoStart/AutoComplete cascades are defined there and assumed below.

Step-by-step

1. Action types: system vs custom

Every task action references a task action type. Each organisation is seeded with five system typesOrder, Inventory, Cyclecount, ItemSearch, Service — used for actions that link to a record in that domain. List them with GET api/taskactiontypes/getfilteredtaskactiontypes (SystemType: true marks the seeded ones).

For free-form manual steps, create a custom type once and reuse it:

curl -sk -X POST "http://<host>/api/taskactiontypes/createtaskactiontype" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"Type":"Photograph shelf"}'

Custom types are always created with SystemType: false — the system flag is server-controlled.

2. Create a task

TaskNo is required and unique per organisation. Assignment is to a user (AssignedToUserId); PlaceId, CustomerId, and ContactId attach location and CRM context but do not gate who can see the task. Each action needs an ActionTypeId; an action with a system type must also carry ActionId — the id of the linked order/stocktake/cycle-count/item-search/service record:

curl -sk -X POST "http://<host>/api/tasks/createtask" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"TaskNo":"TASK-0042","Name":"Weekly shelf audit","Sequence":1,
"DueDate":"2026-07-14T09:00:00Z",
"AssignedToUserId":"<userId>","PlaceId":"<storePlaceId>",
"TaskActions":[
{"ActionCode":"PHOTO","Description":"Photograph the promo shelf",
"Sequence":1,"ActionTypeId":"<photographShelfTypeId>"},
{"ActionCode":"PROCESS","Description":"Process the replenishment order",
"Sequence":2,"ActionTypeId":"<orderSystemTypeId>",
"ActionId":"<orderId>"}]}'

Returns 200 with the TaskDetailDto — the task and every action start in ToDo. There is no Status field on create. AutoStart and AutoComplete both default to true; set them false only if you want the task's state driven exclusively by the explicit lifecycle endpoints in step 5.

DueDate is informational — nothing server-side fires when it passes; build overdue views by filtering on it (step 6).

3. Work the queue (assignee self-service)

The assignee reads their own work without needing Task read permission:

# my open tasks (OData-filterable)
curl -sk "http://<host>/api/tasks/getmytasks?%24filter=Status%20ne%20%27Completed%27" \
-H "Authorization: Token $TOKEN"

# badge count: my tasks not yet Completed/Cancelled
curl -sk "http://<host>/api/tasks/getmyopentaskcount" \
-H "Authorization: Token $TOKEN"

(getmyopentaskcount returns a bare integer.) An assignee can also progress their own task via updatetaskstatus — it accepts the task Status plus a list of action statuses in one call. A caller who is neither the assignee nor holds Task update permission gets 403.

4. Progress actions — the cascade does the rest

The normal completion path is per-action:

curl -sk -X POST "http://<host>/api/tasks/updatetaskactionstatus" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"Id":"<taskActionId>","Status":"Completed"}'

Action statuses are ToDo, InProgress, Completed, NoActionRequired (counts as done). With the default flags, the first action leaving ToDo auto-starts the task (InProgress + StartTime); when all actions are Completed/NoActionRequired the task auto-completes (Completed + EndTime + completing user). The response is the parent TaskDetailDto, so you see any cascaded task-state change immediately.

The Comments field on an action-status update is accepted but not currently persisted — treat it as reserved. Attach real work evidence as media (POST api/tasks/uploadmedia, multipart) or in the task's Notes/extended properties via updatetask.

Cross-domain auto-complete: for a system-typed action, you never call updatetaskactionstatus yourself — completing the linked record (order, stocktake, cycle count, item search, or service record) completes the action server-side, and the cascade runs exactly as above. This is the counterpart to the TaskId/TaskName fields on order summaries — see the concepts page.

Retail vs Asset

The task framework is domain-neutral, but the Service system action type links to a service record — asset-servicing workflow (maintenance, deliver/pickup of tracked assets), covered in Asset servicing. Retail tenants typically link actions to orders, stocktakes, and cycle counts instead.

5. Manual lifecycle endpoints

When you drive state explicitly (or the auto flags are off):

6. Querying, filtering, exporting

GET api/tasks/getfilteredtasks is the org-wide OData list (Task read permission, not place-visibility filtered). Useful filters over TaskSummaryDto columns:

# overdue, still open
curl -sk "http://<host>/api/tasks/getfilteredtasks?%24filter=DueDate%20lt%202026-07-07T00:00:00Z%20and%20Status%20ne%20%27Completed%27%20and%20Status%20ne%20%27Cancelled%27&%24orderby=DueDate" \
-H "Authorization: Token $TOKEN"

Rows carry flattened context (PlaceName, AssignedToUserFullName, TaskScheduleNo, …). Single task: gettask/{id}. CSV export: exportfilteredtasks.

Group tasks under a schedule

A task schedule is a named run — "Week 28 store audits" — with its own Pending → InProgress → Complete/Cancelled lifecycle that mirrors its children's (note the schedule's terminal state is Complete, not Completed):

curl -sk -X POST "http://<host>/api/taskschedules/createtaskschedule" \
-H "Authorization: Token $TOKEN" -H "Content-Type: application/json" \
-d '{"ScheduleNo":"RUN-2026-28","RunNo":"1",
"Description":"Week 28 store audits",
"RunDate":"2026-07-13T00:00:00Z",
"AssignedToUserId":"<supervisorUserId>"}'

Then create each child task with "TaskScheduleId":"<scheduleId>" — that is the only way tasks join a schedule, and it happens at task create.

A schedule is a grouping container, not a recurrence engine

A task schedule does not spawn tasks — there is no CRON or RunDate-driven generation; RunDate is descriptive metadata. If you need "every Monday" tasks, your client (or an external scheduler) creates them each cycle, stamping ScheduleNo/RunNo to keep runs queryable. See the concepts page.

Schedule lifecycle mirrors the task endpoints: starttaskschedule, endtaskschedule (manual complete from InProgress, regardless of children), canceltaskschedule. The schedule's own auto-start/auto-complete cascade from child task states only re-evaluates during updatetaskschedule — completing child tasks does not push the schedule forward by itself.

Query with getfilteredtaskschedules or the assignee's getmytaskschedules (any authenticated user); fetch one — with its child Tasks list — via gettaskschedule/{id}. The assignee badge count is getmyopentaskSchedulecount (the mixed-case route is literal).

Error handling

All failures follow the platform-wide error and response conventions400 with a JSON array of { ErrorCode, Message, Field }.

SituationResponse
Missing TaskNo on create400 MissingRequiredField
Duplicate TaskNo (per organisation)400 "already exists"
Unknown AssignedToUserId, ReasonId (must be a Task-scope reason), or per-action ActionTypeId400 InvalidReference
System-typed action missing its linked-entity ActionId400 InvalidReference
starttask on a task not in ToDo400 "Task already started"
endtask on a Cancelled task / canceltask on a terminal task400 InvalidOperation
endtask on an already-Completed task200 idempotent no-op
updatetaskstatus by a caller who is neither assignee nor Task-update-permitted403
Get/update/delete with an unknown task id400 NotFound
updatetaskschedule with an unknown schedule id500 (known gap — no null guard; other schedule endpoints return 400 NotFound)
No token / wrong scheme (Bearer)401